public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH v2] Missed function specialization + partial devirtualization
@ 2019-07-12  9:19 Xiong Hu Luo
  2019-07-15  8:20 ` Martin Liška
  0 siblings, 1 reply; 38+ messages in thread
From: Xiong Hu Luo @ 2019-07-12  9:19 UTC (permalink / raw)
  To: gcc-patches; +Cc: hubicka, mliska, segher, wschmidt, luoxhu

This patch aims to fix PR69678 caused by PGO indirect call profiling
performance issues.
Currently the default profile can only use the single indirect target
that called more than 50%.  This patch leverages multiple indirect
targets use in LTO-WPA and LTO-LTRANS stage, as a result, function
specialization, profiling, partial devirtualization, inlining and cloning could
be done successfully based on it.
Performance can get improved 3x (1.7 sec -> 0.4 sec) on simple tests.
Details are:
  1.  PGO with topn is enbaled by default now, but the gcda data format is not
  supported in ipa-profile pass, so add variables to pass the information
  through passes, speculative_id will record the direct edge index bind to
  the indirect edge, num_of_ics record how many direct edges owned by
  the indirect edge, postpone gimple_ic to ipa-profile like default as inline
  pass will decide whether it is benefit to transform indirect call.
  2.  Enable LTO WPA/LTRANS stage multiple indirect call targets analysis for
  profile full support in ipa passes and cgraph_edge functions.  speculative_id
  can be set by make_speculative id when multiple targets are binded to
  one indirect edge, and cloned if new edge is cloned.  speculative_id
  is streamed out and stream int by lto like lto_stmt_uid.
  3.  Add 1 in module testcase and 2 cross module testcases.
  4.  Performance result for ppc64le:
    4.1.  Representative test: indir-call-prof-topn.c runtime improved from
    1.7s to 0.4s.
    4.2.  SPEC2017 peakrate:
        523.xalancbmk_r (+4.87%); 538.imagick_r (+4.59%); 511.povray_r (+13.33%);
        525.x264_r (-5.29%).
        No big changes of other benchmarks.
        Option: -Ofast -mcpu=power8
        PASS1_OPTIMIZE: -fprofile-generate
        PASS2_OPTIMIZE: -fprofile-use -fprofile-correction
    4.3.  No performance change on PHP benchmark.
  5.  Bootstrap and regression test passed on Power8-LE.

v2 Changes:
 1. Use speculative_id to track and search the reference node matched
 with the direct edge's callee for multiple targets.  This could
 eliminate the workaround strstr before.  Actually, it is the caller's
 response to handle the direct edges mapped to same indirect edge.
 speculative_call_info will still return one of the direct edge
 specified, this will leverage current IPA edge process framework mostly.
 2. Remove duplicate logic in ipa-profile.  As
 get_most_common_single_value could only return single value, but this
 multiple indirect call needs store each hist value, will
 consider specialize it later.

gcc/ChangeLog

	2019-06-17  Xiong Hu Luo  <luoxhu@linux.ibm.com>

	PR ipa/69678
	* cgraph.c (symbol_table::create_edge): Init speculative_id.
	(cgraph_edge::make_speculative): Add param for setting speculative_id.
	(cgraph_edge::speculative_call_info): Find reference by
	speculative_id for multiple indirect targets.
	(cgraph_edge::resolve_speculation): Decrease the speculations
	for indirect edge, drop it's speculative if not direct target
	left.
	(cgraph_edge::redirect_call_stmt_to_callee): Likewise.
	(cgraph_node::verify_node): Don't report error if speculative
	edge not include statement.
	* cgraph.h (struct indirect_target_info): New struct.
	(indirect_call_targets): New vector variable.
	(num_of_ics): New variable.
	(make_speculative): Add param for setting speculative_id.
	(speculative_id): New variable.
	* cgraphclones.c (cgraph_node::create_clone): Clone speculative_id.
	* ipa-inline.c (inline_small_functions): Add iterator update.
	* ipa-profile.c (ipa_profile_generate_summary): Add indirect
	multiple targets logic.
	(ipa_profile): Likewise.
	* ipa-ref.h (speculative_id): New variable.
	* ipa.c (process_references): Fix typo.
	* lto-cgraph.c (lto_output_edge): Add indirect multiple targets
	logic.  Stream out speculative_id.
	(input_edge): Likewise.
	* predict.c (dump_prediction): Revome edges count assert to be
	precise.
	* symtab.c (symtab_node::create_reference): Init speculative_id.
	(symtab_node::clone_references): Clone speculative_id.
	(symtab_node::clone_referring): Clone speculative_id.
	(symtab_node::clone_reference): Clone speculative_id.
	(symtab_node::clear_stmts_in_references): Clear speculative_id.
	* tree-inline.c (copy_bb): Duplicate all the speculative edges
	if indirect call contains multiple speculative targets.
	* tree-profile.c (gimple_gen_ic_profiler): Use the new variable
	__gcov_indirect_call.counters and __gcov_indirect_call.callee.
	(gimple_gen_ic_func_profiler): Likewise.
	(pass_ipa_tree_profile::gate): Fix comment typos.
	* value-prof.c (check_counter): Proportion the counter for
	multiple targets.
	(gimple_ic_transform): Handle topn case, fix comment typos.

gcc/testsuite/ChangeLog

	2019-06-17  Xiong Hu Luo  <luoxhu@linux.ibm.com>

	PR ipa/69678
	* gcc.dg/tree-prof/indir-call-prof-topn.c: New testcase.
	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c: New testcase.
	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c: New testcase.
	* gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c: New testcase.
---
 gcc/cgraph.c                                  |  70 ++++-
 gcc/cgraph.h                                  |  29 ++-
 gcc/cgraphclones.c                            |   1 +
 gcc/ipa-inline.c                              |   3 +
 gcc/ipa-profile.c                             | 244 ++++++++++--------
 gcc/ipa-ref.h                                 |   1 +
 gcc/ipa.c                                     |   2 +-
 gcc/lto-cgraph.c                              |  54 +++-
 gcc/predict.c                                 |   1 -
 gcc/symtab.c                                  |   5 +
 .../tree-prof/crossmodule-indir-call-topn-1.c |  35 +++
 .../crossmodule-indir-call-topn-1a.c          |  22 ++
 .../tree-prof/crossmodule-indir-call-topn-2.c |  42 +++
 .../gcc.dg/tree-prof/indir-call-prof-topn.c   |  38 +++
 gcc/tree-inline.c                             |  20 ++
 gcc/tree-profile.c                            |  12 +-
 gcc/value-prof.c                              | 140 ++++++----
 17 files changed, 541 insertions(+), 178 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c

diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index a7e3bcf2132..d7be9e9c545 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -869,6 +869,7 @@ symbol_table::create_edge (cgraph_node *caller, cgraph_node *callee,
   edge->prev_callee = NULL;
   edge->next_callee = NULL;
   edge->lto_stmt_uid = 0;
+  edge->speculative_id = 0;
 
   edge->count = count;
 
@@ -1063,7 +1064,8 @@ cgraph_edge::remove (void)
    Return direct edge created.  */
 
 cgraph_edge *
-cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
+cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count,
+			       unsigned int speculative_id)
 {
   cgraph_node *n = caller;
   ipa_ref *ref = NULL;
@@ -1081,11 +1083,13 @@ cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
   else
     e2->can_throw_external = can_throw_external;
   e2->lto_stmt_uid = lto_stmt_uid;
+  e2->speculative_id = speculative_id;
   e2->in_polymorphic_cdtor = in_polymorphic_cdtor;
   count -= e2->count;
   symtab->call_edge_duplication_hooks (this, e2);
   ref = n->create_reference (n2, IPA_REF_ADDR, call_stmt);
   ref->lto_stmt_uid = lto_stmt_uid;
+  ref->speculative_id = speculative_id;
   ref->speculative = speculative;
   n2->mark_address_taken ();
   return e2;
@@ -1099,6 +1103,38 @@ cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
    call) and if one of them exists, all of them must exist.
 
    Given speculative call edge, return all three components.
+
+   For some indirect edge, it may maps to multiple direct edges, i.e. 1:N.
+   check the speculative_id to return all the three components for specified
+   direct edge or indirect edge.
+   If input is indirect, caller of this function will get the direct edge one by
+   one, get_edge will just return one of the direct edge mapped to the indirect
+   edge, the returned direct edge will be resolved or redirected by the caller,
+   then num_of_ics (speculations) is deceased in each access.
+   If input is direct, this function will get the indirect edge and reference
+   with matched speculative_id, the returned edge will also be resolved or
+   redirected, decrease the speculations accordingly.
+   Speculations of indirect edge will be dropped only if all direct edges
+   be handled.
+
+   e.g. for indirect edge E statement "call call_dest":
+
+   Redirect N3 after redirected N2:
+
+   if (call_dest == N2)
+     n2 ();
+   else if (call_dest == N3)
+     n3 ();
+   else
+     call call_dest
+
+   Resolve N3 and only redirect N2:
+
+   if (call_dest == N2)
+     n2 ();
+   else
+     call call_dest
+
  */
 
 void
@@ -1138,7 +1174,7 @@ cgraph_edge::speculative_call_info (cgraph_edge *&direct,
 
   reference = NULL;
   for (i = 0; e->caller->iterate_reference (i, ref); i++)
-    if (ref->speculative
+    if (ref->speculative && ref->speculative_id == e->speculative_id
 	&& ((ref->stmt && ref->stmt == e->call_stmt)
 	    || (!ref->stmt && ref->lto_stmt_uid == e->lto_stmt_uid)))
       {
@@ -1199,7 +1235,19 @@ cgraph_edge::resolve_speculation (tree callee_decl)
          in the functions inlined through it.  */
     }
   edge->count += e2->count;
-  edge->speculative = false;
+  /* edge is indirect, e2 is direct.  If edge contains multiple speculations,
+     remove one of speculations for this indirect edge, then if edge still
+     contains direct target, keep the speculation, next direct target
+     will continue use it.  Give up speculation completely if no direct
+     target is left for this indirect edge.  */
+  if (edge->indirect_info && edge->indirect_info->num_of_ics)
+    {
+      edge->indirect_info->num_of_ics--;
+      if (edge->indirect_info->num_of_ics == 0)
+	edge->speculative = false;
+    }
+  else
+    edge->speculative = false;
   e2->speculative = false;
   ref->remove_reference ();
   if (e2->indirect_unknown_callee || e2->inline_failed)
@@ -1333,7 +1381,19 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
 	  e->caller->set_call_stmt_including_clones (e->call_stmt, new_stmt,
 						     false);
 	  e->count = gimple_bb (e->call_stmt)->count;
-	  e2->speculative = false;
+	  /* edge is direct, e2 is indirect here.  If e2 contains multiple
+	     speculations, remove one of speculations for this indirect edge,
+	     then if e2 still contains direct target, keep the speculation,
+	     next direct target will continue use it.  Give up speculation
+	     completely if no direct target is left for this indirect e2.  */
+	  if (e2->indirect_info && e2->indirect_info->num_of_ics)
+	    {
+	      e2->indirect_info->num_of_ics--;
+	      if (e2->indirect_info->num_of_ics == 0)
+		e2->speculative = false;
+	    }
+	  else
+	    e2->speculative = false;
 	  e2->count = gimple_bb (e2->call_stmt)->count;
 	  ref->speculative = false;
 	  ref->stmt = NULL;
@@ -3434,7 +3494,7 @@ cgraph_node::verify_node (void)
 
       for (e = callees; e; e = e->next_callee)
 	{
-	  if (!e->aux)
+	  if (!e->aux && !e->speculative)
 	    {
 	      error ("edge %s->%s has no corresponding call_stmt",
 		     identifier_to_locale (e->caller->name ()),
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index fa5224fb3a5..615e8959ab0 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -24,6 +24,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "profile-count.h"
 #include "ipa-ref.h"
 #include "plugin-api.h"
+#include "gcov-io.h"
 
 extern void debuginfo_early_init (void);
 extern void debuginfo_init (void);
@@ -1630,6 +1631,16 @@ private:
   void make_speculative (tree otr_type = NULL);
 };
 
+/* Structure containing indirect target information from profile.  */
+
+struct GTY (()) indirect_target_info
+{
+  /* Profile_id of common target obtained from profile.  */
+  unsigned int common_target_id;
+  /* Probability that call will land in function with COMMON_TARGET_ID.  */
+  int common_target_probability;
+};
+
 /* Structure containing additional information about an indirect call.  */
 
 class GTY(()) cgraph_indirect_call_info
@@ -1648,10 +1659,14 @@ public:
   int param_index;
   /* ECF flags determined from the caller.  */
   int ecf_flags;
-  /* Profile_id of common target obtrained from profile.  */
-  int common_target_id;
-  /* Probability that call will land in function with COMMON_TARGET_ID.  */
-  int common_target_probability;
+
+  /* An indirect call may contain one or multiple call targets.  */
+  vec<indirect_target_info, va_gc> *indirect_call_targets;
+
+  /* Number of indirect targets (speculations) for the indirect call.
+     Increased by ipa-profile, decreased by resolve_speculation or redirect
+     statement to callee.  */
+  unsigned num_of_ics;
 
   /* Set when the call is a virtual call with the parameter being the
      associated object pointer rather than a simple direct call.  */
@@ -1708,7 +1723,8 @@ public:
   /* Turn edge into speculative call calling N2. Update
      the profile so the direct call is taken COUNT times
      with FREQUENCY.  */
-  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count);
+  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count,
+				 unsigned int speculative_id = 0);
 
    /* Given speculative call edge, return all three components.  */
   void speculative_call_info (cgraph_edge *&direct, cgraph_edge *&indirect,
@@ -1786,6 +1802,9 @@ public:
   /* The stmt_uid of call_stmt.  This is used by LTO to recover the call_stmt
      when the function is serialized in.  */
   unsigned int lto_stmt_uid;
+  /* speculative id is used by multiple indirect targets when the function is
+   speculated.  */
+  unsigned int speculative_id;
   /* Whether this edge was made direct by indirect inlining.  */
   unsigned int indirect_inlining_edge : 1;
   /* Whether this edge describes an indirect call with an undetermined
diff --git a/gcc/cgraphclones.c b/gcc/cgraphclones.c
index fd867ecac91..ab6b9dd86c8 100644
--- a/gcc/cgraphclones.c
+++ b/gcc/cgraphclones.c
@@ -128,6 +128,7 @@ cgraph_edge::clone (cgraph_node *n, gcall *call_stmt, unsigned stmt_uid,
   new_edge->inline_failed = inline_failed;
   new_edge->indirect_inlining_edge = indirect_inlining_edge;
   new_edge->lto_stmt_uid = stmt_uid;
+  new_edge->speculative_id = speculative_id;
   /* Clone flags that depend on call_stmt availability manually.  */
   new_edge->can_throw_external = can_throw_external;
   new_edge->call_stmt_cannot_inline_p = call_stmt_cannot_inline_p;
diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
index 939d86ef94a..d82f7f28376 100644
--- a/gcc/ipa-inline.c
+++ b/gcc/ipa-inline.c
@@ -1877,12 +1877,15 @@ inline_small_functions (void)
 	}
       if (has_speculative)
 	for (edge = node->callees; edge; edge = next)
+	{
+	  next = edge->next_callee;
 	  if (edge->speculative && !speculation_useful_p (edge,
 							  edge->aux != NULL))
 	    {
 	      edge->resolve_speculation ();
 	      update = true;
 	    }
+	}
       if (update)
 	{
 	  struct cgraph_node *where = node->global.inlined_to
diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
index 1fb939b73d0..c6ac7400422 100644
--- a/gcc/ipa-profile.c
+++ b/gcc/ipa-profile.c
@@ -191,26 +191,40 @@ ipa_profile_generate_summary (void)
 		     takes away bad histograms.  */
 		  if (h)
 		    {
-		      gcov_type val, count, all;
-		      if (get_most_common_single_value (NULL, "indirect call",
-							h, &val, &count, &all))
+		      unsigned j;
+		      struct cgraph_edge *e = node->get_edge (stmt);
+		      if (e && !e->indirect_unknown_callee)
+			continue;
+
+		      e->indirect_info->num_of_ics = 0;
+		      for (j = 1; j < h->n_counters; j += 2)
 			{
-			  struct cgraph_edge * e = node->get_edge (stmt);
-			  if (e && !e->indirect_unknown_callee)
+			  if (h->hvalue.counters[j] == 0)
 			    continue;
 
-			  e->indirect_info->common_target_id = val;
-			  e->indirect_info->common_target_probability
-			    = GCOV_COMPUTE_SCALE (count, all);
-			  if (e->indirect_info->common_target_probability > REG_BR_PROB_BASE)
+			  struct indirect_target_info item;
+			  item.common_target_id = h->hvalue.counters[j];
+			  item.common_target_probability = GCOV_COMPUTE_SCALE (
+			    h->hvalue.counters[j + 1],
+			    gimple_bb (stmt)->count.ipa ().to_gcov_type ());
+			  vec_safe_push (
+			    e->indirect_info->indirect_call_targets, item);
+
+			  if (item.common_target_probability > REG_BR_PROB_BASE)
 			    {
 			      if (dump_file)
-				fprintf (dump_file, "Probability capped to 1\n");
-			      e->indirect_info->common_target_probability = REG_BR_PROB_BASE;
+				fprintf (dump_file,
+					 "Probability capped to 1\n");
+			      item.common_target_probability = REG_BR_PROB_BASE;
 			    }
+			  e->indirect_info->num_of_ics++;
 			}
-		      gimple_remove_histogram_value (DECL_STRUCT_FUNCTION (node->decl),
-						      stmt, h);
+		      gcc_assert (e->indirect_info->num_of_ics
+				  <= GCOV_TOPN_VALUES);
+
+		      gimple_remove_histogram_value (DECL_STRUCT_FUNCTION (
+						       node->decl),
+						     stmt, h);
 		    }
 		}
 	      time += estimate_num_insns (stmt, &eni_time_weights);
@@ -492,6 +506,7 @@ ipa_profile (void)
   int nindirect = 0, ncommon = 0, nunknown = 0, nuseless = 0, nconverted = 0;
   int nmismatch = 0, nimpossible = 0;
   bool node_map_initialized = false;
+  gcov_type threshold;
 
   if (dump_file)
     dump_histogram (dump_file, histogram);
@@ -500,14 +515,12 @@ ipa_profile (void)
       overall_time += histogram[i]->count * histogram[i]->time;
       overall_size += histogram[i]->size;
     }
+  threshold = 0;
   if (overall_time)
     {
-      gcov_type threshold;
-
       gcc_assert (overall_size);
 
       cutoff = (overall_time * PARAM_VALUE (HOT_BB_COUNT_WS_PERMILLE) + 500) / 1000;
-      threshold = 0;
       for (i = 0; cumulated < cutoff; i++)
 	{
 	  cumulated += histogram[i]->count * histogram[i]->time;
@@ -543,7 +556,7 @@ ipa_profile (void)
   histogram.release ();
   histogram_pool.release ();
 
-  /* Produce speculative calls: we saved common traget from porfiling into
+  /* Produce speculative calls: we saved common target from profiling into
      e->common_target_id.  Now, at link time, we can look up corresponding
      function node and produce speculative call.  */
 
@@ -558,104 +571,133 @@ ipa_profile (void)
 	{
 	  if (n->count.initialized_p ())
 	    nindirect++;
-	  if (e->indirect_info->common_target_id)
+	  if (e->indirect_info && e->indirect_info->num_of_ics)
 	    {
-	      if (!node_map_initialized)
-	        init_node_map (false);
-	      node_map_initialized = true;
-	      ncommon++;
-	      n2 = find_func_by_profile_id (e->indirect_info->common_target_id);
-	      if (n2)
+	      if (in_lto_p)
 		{
 		  if (dump_file)
 		    {
-		      fprintf (dump_file, "Indirect call -> direct call from"
-			       " other module %s => %s, prob %3.2f\n",
-			       n->dump_name (),
-			       n2->dump_name (),
-			       e->indirect_info->common_target_probability
-			       / (float)REG_BR_PROB_BASE);
-		    }
-		  if (e->indirect_info->common_target_probability
-		      < REG_BR_PROB_BASE / 2)
-		    {
-		      nuseless++;
-		      if (dump_file)
-			fprintf (dump_file,
-				 "Not speculating: probability is too low.\n");
-		    }
-		  else if (!e->maybe_hot_p ())
-		    {
-		      nuseless++;
-		      if (dump_file)
-			fprintf (dump_file,
-				 "Not speculating: call is cold.\n");
+		      fprintf (dump_file,
+			       "Updating hotness threshold in LTO mode.\n");
+		      fprintf (dump_file, "Updated min count: %" PRId64 "\n",
+			       (int64_t) threshold);
 		    }
-		  else if (n2->get_availability () <= AVAIL_INTERPOSABLE
-			   && n2->can_be_discarded_p ())
-		    {
-		      nuseless++;
-		      if (dump_file)
-			fprintf (dump_file,
-				 "Not speculating: target is overwritable "
-				 "and can be discarded.\n");
-		    }
-		  else if (ipa_node_params_sum && ipa_edge_args_sum
-			   && (!vec_safe_is_empty
-			       (IPA_NODE_REF (n2)->descriptors))
-			   && ipa_get_param_count (IPA_NODE_REF (n2))
-			      != ipa_get_cs_argument_count (IPA_EDGE_REF (e))
-			    && (ipa_get_param_count (IPA_NODE_REF (n2))
-				>= ipa_get_cs_argument_count (IPA_EDGE_REF (e))
-				|| !stdarg_p (TREE_TYPE (n2->decl))))
+		  set_hot_bb_threshold (threshold
+					/ e->indirect_info->num_of_ics);
+		}
+	      if (!node_map_initialized)
+		init_node_map (false);
+	      node_map_initialized = true;
+	      ncommon++;
+	      unsigned speculative_id = 0;
+	      struct indirect_target_info *item;
+	      FOR_EACH_VEC_SAFE_ELT (e->indirect_info->indirect_call_targets, i,
+				     item)
+		{
+		  if ((unsigned) i > e->indirect_info->num_of_ics)
+		    break;
+		  n2 = find_func_by_profile_id (item->common_target_id);
+		  if (n2)
 		    {
-		      nmismatch++;
 		      if (dump_file)
-			fprintf (dump_file,
-				 "Not speculating: "
-				 "parameter count mistmatch\n");
+			{
+			  fprintf (dump_file,
+				   "Indirect call -> direct call from"
+				   " other module %s => %s, prob %3.2f\n",
+				   n->dump_name (), n2->dump_name (),
+				   item->common_target_probability
+				     / (float) REG_BR_PROB_BASE);
+			}
+		      if (item->common_target_probability
+			  < REG_BR_PROB_BASE / 2)
+			{
+			  nuseless++;
+			  if (dump_file)
+			    fprintf (
+			      dump_file,
+			      "Not speculating: probability is too low.\n");
+			}
+		      else if (!e->maybe_hot_p ())
+			{
+			  nuseless++;
+			  if (dump_file)
+			    fprintf (dump_file,
+				     "Not speculating: call is cold.\n");
+			}
+		      else if (n2->get_availability () <= AVAIL_INTERPOSABLE
+			       && n2->can_be_discarded_p ())
+			{
+			  nuseless++;
+			  if (dump_file)
+			    fprintf (dump_file,
+				     "Not speculating: target is overwritable "
+				     "and can be discarded.\n");
+			}
+		      else if (ipa_node_params_sum && ipa_edge_args_sum
+			       && (!vec_safe_is_empty (
+				 IPA_NODE_REF (n2)->descriptors))
+			       && ipa_get_param_count (IPA_NODE_REF (n2))
+				    != ipa_get_cs_argument_count (
+				      IPA_EDGE_REF (e))
+			       && (ipa_get_param_count (IPA_NODE_REF (n2))
+				     >= ipa_get_cs_argument_count (
+				       IPA_EDGE_REF (e))
+				   || !stdarg_p (TREE_TYPE (n2->decl))))
+			{
+			  nmismatch++;
+			  if (dump_file)
+			    fprintf (dump_file, "Not speculating: "
+						"parameter count mismatch\n");
+			}
+		      else if (e->indirect_info->polymorphic
+			       && !opt_for_fn (n->decl, flag_devirtualize)
+			       && !possible_polymorphic_call_target_p (e, n2))
+			{
+			  nimpossible++;
+			  if (dump_file)
+			    fprintf (dump_file,
+				     "Not speculating: "
+				     "function is not in the polymorphic "
+				     "call target list\n");
+			}
+		      else
+			{
+			  /* Target may be overwritable, but profile says that
+			     control flow goes to this particular implementation
+			     of N2.  Speculate on the local alias to allow
+			     inlining.
+			     */
+			  if (!n2->can_be_discarded_p ())
+			    {
+			      cgraph_node *alias;
+			      alias = dyn_cast<cgraph_node *> (
+				n2->noninterposable_alias ());
+			      if (alias)
+				n2 = alias;
+			    }
+			  nconverted++;
+			  e->make_speculative (
+			    n2,
+			    e->count.apply_probability (
+			      item->common_target_probability),
+			    speculative_id);
+			  update = true;
+			  speculative_id++;
+			}
 		    }
-		  else if (e->indirect_info->polymorphic
-			   && !opt_for_fn (n->decl, flag_devirtualize)
-			   && !possible_polymorphic_call_target_p (e, n2))
+		  else
 		    {
-		      nimpossible++;
 		      if (dump_file)
 			fprintf (dump_file,
-				 "Not speculating: "
-				 "function is not in the polymorphic "
-				 "call target list\n");
-		    }
-		  else
-		    {
-		      /* Target may be overwritable, but profile says that
-			 control flow goes to this particular implementation
-			 of N2.  Speculate on the local alias to allow inlining.
-		       */
-		      if (!n2->can_be_discarded_p ())
-			{
-			  cgraph_node *alias;
-			  alias = dyn_cast<cgraph_node *> (n2->noninterposable_alias ());
-			  if (alias)
-			    n2 = alias;
-			}
-		      nconverted++;
-		      e->make_speculative
-			(n2,
-			 e->count.apply_probability
-				     (e->indirect_info->common_target_probability));
-		      update = true;
+				 "Function with profile-id %i not found.\n",
+				 item->common_target_id);
+		      nunknown++;
 		    }
 		}
-	      else
-		{
-		  if (dump_file)
-		    fprintf (dump_file, "Function with profile-id %i not found.\n",
-			     e->indirect_info->common_target_id);
-		  nunknown++;
-		}
+	      if (speculative_id < e->indirect_info->num_of_ics)
+		e->indirect_info->num_of_ics = speculative_id;
 	    }
-	 }
+	}
        if (update)
 	 ipa_update_overall_fn_summary (n);
      }
diff --git a/gcc/ipa-ref.h b/gcc/ipa-ref.h
index 0d8e509c932..3e6562ec9d1 100644
--- a/gcc/ipa-ref.h
+++ b/gcc/ipa-ref.h
@@ -59,6 +59,7 @@ public:
   symtab_node *referred;
   gimple *stmt;
   unsigned int lto_stmt_uid;
+  unsigned int speculative_id;
   unsigned int referred_index;
   ENUM_BITFIELD (ipa_ref_use) use:3;
   unsigned int speculative:1;
diff --git a/gcc/ipa.c b/gcc/ipa.c
index 2496694124c..c1fe081a72d 100644
--- a/gcc/ipa.c
+++ b/gcc/ipa.c
@@ -166,7 +166,7 @@ process_references (symtab_node *snode,
    devirtualization happens.  After inlining still keep their declarations
    around, so we can devirtualize to a direct call.
 
-   Also try to make trivial devirutalization when no or only one target is
+   Also try to make trivial devirtualization when no or only one target is
    possible.  */
 
 static void
diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
index bc0f0107333..1dc856d3c64 100644
--- a/gcc/lto-cgraph.c
+++ b/gcc/lto-cgraph.c
@@ -238,6 +238,7 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
   unsigned int uid;
   intptr_t ref;
   struct bitpack_d bp;
+  unsigned len;
 
   if (edge->indirect_unknown_callee)
     streamer_write_enum (ob->main_stream, LTO_symtab_tags, LTO_symtab_last_tag,
@@ -265,6 +266,7 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
   bp_pack_enum (&bp, cgraph_inline_failed_t,
 	        CIF_N_REASONS, edge->inline_failed);
   bp_pack_var_len_unsigned (&bp, uid);
+  bp_pack_var_len_unsigned (&bp, edge->speculative_id);
   bp_pack_value (&bp, edge->indirect_inlining_edge, 1);
   bp_pack_value (&bp, edge->speculative, 1);
   bp_pack_value (&bp, edge->call_stmt_cannot_inline_p, 1);
@@ -291,11 +293,28 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
   streamer_write_bitpack (&bp);
   if (edge->indirect_unknown_callee)
     {
+      struct indirect_target_info *item;
+      unsigned int i;
+      len = edge->indirect_info->num_of_ics;
+      gcc_assert (len <= GCOV_TOPN_VALUES);
+
       streamer_write_hwi_stream (ob->main_stream,
-			         edge->indirect_info->common_target_id);
-      if (edge->indirect_info->common_target_id)
-	streamer_write_hwi_stream
-	   (ob->main_stream, edge->indirect_info->common_target_probability);
+				 edge->indirect_info->num_of_ics);
+
+      if (len)
+	{
+	  FOR_EACH_VEC_SAFE_ELT (edge->indirect_info->indirect_call_targets, i,
+				 item)
+	    {
+	      if (i == edge->indirect_info->num_of_ics)
+		break;
+	      streamer_write_hwi_stream (ob->main_stream,
+					 item->common_target_id);
+	      if (item->common_target_id)
+		streamer_write_hwi_stream (ob->main_stream,
+					   item->common_target_probability);
+	    }
+	}
     }
 }
 
@@ -688,6 +707,7 @@ lto_output_ref (struct lto_simple_output_block *ob, struct ipa_ref *ref,
       if (ref->stmt)
 	uid = gimple_uid (ref->stmt) + 1;
       streamer_write_hwi_stream (ob->main_stream, uid);
+      streamer_write_hwi_stream (ob->main_stream, ref->speculative_id);
     }
 }
 
@@ -1420,6 +1440,8 @@ input_ref (class lto_input_block *ib,
   ref->speculative = speculative;
   if (is_a <cgraph_node *> (referring_node))
     ref->lto_stmt_uid = streamer_read_hwi (ib);
+  if (is_a <cgraph_node *> (referring_node))
+    ref->speculative_id = streamer_read_hwi (ib);
 }
 
 /* Read an edge from IB.  NODES points to a vector of previously read nodes for
@@ -1433,11 +1455,12 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
 {
   struct cgraph_node *caller, *callee;
   struct cgraph_edge *edge;
-  unsigned int stmt_id;
+  unsigned int stmt_id, speculative_id;
   profile_count count;
   cgraph_inline_failed_t inline_failed;
   struct bitpack_d bp;
   int ecf_flags = 0;
+  unsigned i, len;
 
   caller = dyn_cast<cgraph_node *> (nodes[streamer_read_hwi (ib)]);
   if (caller == NULL || caller->decl == NULL_TREE)
@@ -1457,6 +1480,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
   bp = streamer_read_bitpack (ib);
   inline_failed = bp_unpack_enum (&bp, cgraph_inline_failed_t, CIF_N_REASONS);
   stmt_id = bp_unpack_var_len_unsigned (&bp);
+  speculative_id = bp_unpack_var_len_unsigned (&bp);
 
   if (indirect)
     edge = caller->create_indirect_edge (NULL, 0, count);
@@ -1466,6 +1490,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
   edge->indirect_inlining_edge = bp_unpack_value (&bp, 1);
   edge->speculative = bp_unpack_value (&bp, 1);
   edge->lto_stmt_uid = stmt_id;
+  edge->speculative_id = speculative_id;
   edge->inline_failed = inline_failed;
   edge->call_stmt_cannot_inline_p = bp_unpack_value (&bp, 1);
   edge->can_throw_external = bp_unpack_value (&bp, 1);
@@ -1485,9 +1510,22 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
       if (bp_unpack_value (&bp, 1))
 	ecf_flags |= ECF_RETURNS_TWICE;
       edge->indirect_info->ecf_flags = ecf_flags;
-      edge->indirect_info->common_target_id = streamer_read_hwi (ib);
-      if (edge->indirect_info->common_target_id)
-        edge->indirect_info->common_target_probability = streamer_read_hwi (ib);
+
+      len = streamer_read_hwi (ib);
+      edge->indirect_info->num_of_ics = len;
+
+      gcc_assert (len <= GCOV_TOPN_VALUES);
+
+      if (len)
+	{
+	  for (i = 0; i < len; i++)
+	    {
+	      indirect_target_info item;
+	      item.common_target_id = streamer_read_hwi (ib);
+	      item.common_target_probability = streamer_read_hwi (ib);
+	      vec_safe_push (edge->indirect_info->indirect_call_targets, item);
+	    }
+	}
     }
 }
 
diff --git a/gcc/predict.c b/gcc/predict.c
index 07f66aab7a3..67a859ea436 100644
--- a/gcc/predict.c
+++ b/gcc/predict.c
@@ -763,7 +763,6 @@ dump_prediction (FILE *file, enum br_predictor predictor, int probability,
       && bb->count.precise_p ()
       && reason == REASON_NONE)
     {
-      gcc_assert (e->count ().precise_p ());
       fprintf (file, ";;heuristics;%s;%" PRId64 ";%" PRId64 ";%.1f;\n",
 	       predictor_info[predictor].name,
 	       bb->count.to_gcov_type (), e->count ().to_gcov_type (),
diff --git a/gcc/symtab.c b/gcc/symtab.c
index b1589ea8a40..90a1f62d6a9 100644
--- a/gcc/symtab.c
+++ b/gcc/symtab.c
@@ -603,6 +603,7 @@ symtab_node::create_reference (symtab_node *referred_node,
   ref->referred = referred_node;
   ref->stmt = stmt;
   ref->lto_stmt_uid = 0;
+  ref->speculative_id = 0;
   ref->use = use_type;
   ref->speculative = 0;
 
@@ -660,6 +661,7 @@ symtab_node::clone_references (symtab_node *node)
       ref2 = create_reference (ref->referred, ref->use, ref->stmt);
       ref2->speculative = speculative;
       ref2->lto_stmt_uid = stmt_uid;
+      ref2->speculative_id = ref->speculative_id;
     }
 }
 
@@ -678,6 +680,7 @@ symtab_node::clone_referring (symtab_node *node)
       ref2 = ref->referring->create_reference (this, ref->use, ref->stmt);
       ref2->speculative = speculative;
       ref2->lto_stmt_uid = stmt_uid;
+      ref2->speculative_id = ref->speculative_id;
     }
 }
 
@@ -693,6 +696,7 @@ symtab_node::clone_reference (ipa_ref *ref, gimple *stmt)
   ref2 = create_reference (ref->referred, ref->use, stmt);
   ref2->speculative = speculative;
   ref2->lto_stmt_uid = stmt_uid;
+  ref2->speculative_id = ref->speculative_id;
   return ref2;
 }
 
@@ -747,6 +751,7 @@ symtab_node::clear_stmts_in_references (void)
       {
 	r->stmt = NULL;
 	r->lto_stmt_uid = 0;
+	r->speculative_id = 0;
       }
 }
 
diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
new file mode 100644
index 00000000000..e0a83c2e067
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
@@ -0,0 +1,35 @@
+/* { dg-require-effective-target lto } */
+/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate --param indir-call-topn-profile=1" } */
+
+#include <stdio.h>
+
+typedef int (*fptr) (int);
+int
+one (int a);
+
+int
+two (int a);
+
+fptr table[] = {&one, &two};
+
+int
+main()
+{
+  int i, x;
+  fptr p = &one;
+
+  x = one (3);
+
+  for (i = 0; i < 350000000; i++)
+    {
+      x = (*p) (3);
+      p = table[x];
+    }
+  printf ("done:%d\n", x);
+}
+
+/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* one transformation on insn" "profile_estimate" } } */
+/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* two transformation on insn" "profile_estimate" } } */
+
diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
new file mode 100644
index 00000000000..a8c6e365fb9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
@@ -0,0 +1,22 @@
+/* It seems there is no way to avoid the other source of mulitple
+   source testcase from being compiled independently.  Just avoid
+   error.  */
+#ifdef DOJOB
+int
+one (int a)
+{
+  return 1;
+}
+
+int
+two (int a)
+{
+  return 0;
+}
+#else
+int
+main()
+{
+  return 0;
+}
+#endif
diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
new file mode 100644
index 00000000000..aa3887fde83
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
@@ -0,0 +1,42 @@
+/* { dg-require-effective-target lto } */
+/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate --param indir-call-topn-profile=1" } */
+
+#include <stdio.h>
+
+typedef int (*fptr) (int);
+int
+one (int a);
+
+int
+two (int a);
+
+fptr table[] = {&one, &two};
+
+int foo ()
+{
+  int i, x;
+  fptr p = &one;
+
+  x = one (3);
+
+  for (i = 0; i < 350000000; i++)
+    {
+      x = (*p) (3);
+      p = table[x];
+    }
+  return x;
+}
+
+int
+main()
+{
+  int x = foo ();
+  printf ("done:%d\n", x);
+}
+
+/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* one transformation on insn" "profile_estimate" } } */
+/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* two transformation on insn" "profile_estimate" } } */
+
+
diff --git a/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
new file mode 100644
index 00000000000..951bc7ddd19
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
@@ -0,0 +1,38 @@
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-options "-O2 -fdump-ipa-profile --param indir-call-topn-profile=1" } */
+
+#include <stdio.h>
+
+typedef int (*fptr) (int);
+int
+one (int a)
+{
+  return 1;
+}
+
+int
+two (int a)
+{
+  return 0;
+}
+
+fptr table[] = {&one, &two};
+
+int
+main()
+{
+  int i, x;
+  fptr p = &one;
+
+  one (3);
+
+  for (i = 0; i < 350000000; i++)
+    {
+      x = (*p) (3);
+      p = table[x];
+    }
+  printf ("done:%d\n", x);
+}
+
+/* { dg-final-use-not-autofdo { scan-ipa-dump "Indirect call -> direct call.* one transformation on insn" "profile" } } */
+/* { dg-final-use-not-autofdo { scan-ipa-dump "Indirect call -> direct call.* two transformation on insn" "profile" } } */
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index 4311309acce..93d9269aa8f 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -2167,6 +2167,26 @@ copy_bb (copy_body_data *id, basic_block bb,
 
 			  gcc_assert (!edge->indirect_unknown_callee);
 			  old_edge->speculative_call_info (direct, indirect, ref);
+			  while (old_edge->next_callee
+				 && old_edge->next_callee->speculative
+				 && indirect->indirect_info
+				 && indirect->indirect_info->num_of_ics > 1)
+			    {
+			      /* Some speculative calls may contain more than
+				 one direct target, loop iterate it to clone all
+				 related direct edges before cloning the related
+				 indirect edge.  */
+			      id->dst_node->clone_reference (ref, stmt);
+
+			      edge = old_edge->next_callee;
+			      edge = edge->clone (id->dst_node, call_stmt,
+						  gimple_uid (stmt), num, den,
+						  true);
+			      old_edge = old_edge->next_callee;
+			      gcc_assert (!edge->indirect_unknown_callee);
+			      old_edge->speculative_call_info (direct, indirect,
+							       ref);
+			    }
 
 			  profile_count indir_cnt = indirect->count;
 			  indirect = indirect->clone (id->dst_node, call_stmt,
diff --git a/gcc/tree-profile.c b/gcc/tree-profile.c
index 554a8c98419..f35bd3f9d7a 100644
--- a/gcc/tree-profile.c
+++ b/gcc/tree-profile.c
@@ -74,8 +74,8 @@ static GTY(()) tree ic_tuple_callee_field;
 /* Do initialization work for the edge profiler.  */
 
 /* Add code:
-   __thread gcov*	__gcov_indirect_call_counters; // pointer to actual counter
-   __thread void*	__gcov_indirect_call_callee; // actual callee address
+   __thread gcov*	__gcov_indirect_call.counters; // pointer to actual counter
+   __thread void*	__gcov_indirect_call.callee; // actual callee address
    __thread int __gcov_function_counter; // time profiler function counter
 */
 static void
@@ -384,7 +384,7 @@ gimple_gen_ic_profiler (histogram_value value, unsigned tag, unsigned base)
       f_1 = foo;
       __gcov_indirect_call.counters = &__gcov4.main[0];
       PROF_9 = f_1;
-      __gcov_indirect_call_callee = PROF_9;
+      __gcov_indirect_call.callee = PROF_9;
       _4 = f_1 ();
    */
 
@@ -447,11 +447,11 @@ gimple_gen_ic_func_profiler (void)
 
   /* Insert code:
 
-     if (__gcov_indirect_call_callee != NULL)
+     if (__gcov_indirect_call.callee != NULL)
        __gcov_indirect_call_profiler_v3 (profile_id, &current_function_decl);
 
      The function __gcov_indirect_call_profiler_v3 is responsible for
-     resetting __gcov_indirect_call_callee to NULL.  */
+     resetting __gcov_indirect_call.callee to NULL.  */
 
   gimple_stmt_iterator gsi = gsi_start_bb (cond_bb);
   void0 = build_int_cst (ptr_type_node, 0);
@@ -893,7 +893,7 @@ pass_ipa_tree_profile::gate (function *)
 {
   /* When profile instrumentation, use or test coverage shall be performed.
      But for AutoFDO, this there is no instrumentation, thus this pass is
-     diabled.  */
+     disabled.  */
   return (!in_lto_p && !flag_auto_profile
 	  && (flag_branch_probabilities || flag_test_coverage
 	      || profile_arc_flag));
diff --git a/gcc/value-prof.c b/gcc/value-prof.c
index 32e6ddd8165..100f3fd9bc6 100644
--- a/gcc/value-prof.c
+++ b/gcc/value-prof.c
@@ -543,8 +543,8 @@ free_histograms (struct function *fn)
    somehow.  */
 
 static bool
-check_counter (gimple *stmt, const char * name,
-	       gcov_type *count, gcov_type *all, profile_count bb_count_d)
+check_counter (gimple *stmt, const char *name, gcov_type *count, gcov_type *all,
+	       profile_count bb_count_d, float ratio = 1.0f)
 {
   gcov_type bb_count = bb_count_d.ipa ().to_gcov_type ();
   if (*all != bb_count || *count > *all)
@@ -563,7 +563,7 @@ check_counter (gimple *stmt, const char * name,
                              "count (%d)\n", name, (int)*all, (int)bb_count);
 	  *all = bb_count;
 	  if (*count > *all)
-            *count = *all;
+	    *count = *all * ratio;
 	  return false;
 	}
       else
@@ -736,7 +736,7 @@ get_most_common_single_value (gimple *stmt, const char *counter_type,
       gcov_type v = hist->hvalue.counters[2 * i + 1];
       gcov_type c = hist->hvalue.counters[2 * i + 2];
 
-      /* Indirect calls can't be vereified.  */
+      /* Indirect calls can't be verified.  */
       if (stmt && check_counter (stmt, counter_type, &c, &read_all,
 				 gimple_bb (stmt)->count))
 	return false;
@@ -1411,19 +1411,21 @@ gimple_ic (gcall *icall_stmt, struct cgraph_node *direct_call,
   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:
- */
+/* There maybe multiple indirect targets in histogram.  Check every
+   indirect/virtual call if callee function exists, if not exist, leave it to
+   LTO stage for later process.  Modify code of this indirect call to an if-else
+   structure in ipa-profile finally.  */
 
 static bool
 gimple_ic_transform (gimple_stmt_iterator *gsi)
 {
   gcall *stmt;
   histogram_value histogram;
-  gcov_type val, count, all;
+  gcov_type val, count, all, count_all, bb_all;
+  profile_count bb_count;
   struct cgraph_node *direct_call;
+  enum hist_type type;
+  unsigned j;
 
   stmt = dyn_cast <gcall *> (gsi_stmt (*gsi));
   if (!stmt)
@@ -1435,58 +1437,94 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
   if (gimple_call_internal_p (stmt))
     return false;
 
-  histogram = gimple_histogram_value_of_type (cfun, stmt, HIST_TYPE_INDIR_CALL);
+  histogram
+    = gimple_histogram_value_of_type (cfun, stmt, HIST_TYPE_INDIR_CALL);
   if (!histogram)
     return false;
 
-  if (!get_most_common_single_value (NULL, "indirect call", histogram, &val,
-				     &count, &all))
-    return false;
-
-  if (4 * count <= 3 * all)
-    return false;
+  count = 0;
+  all = histogram->hvalue.counters[0];
+  bb_all = gimple_bb (stmt)->count.ipa ().to_gcov_type ();
+  bb_count = gimple_bb (stmt)->count;
 
-  direct_call = find_func_by_profile_id ((int)val);
+  /* n_counters need be odd to avoid access violation.  */
+  gcc_assert (histogram->n_counters % 2 == 1);
 
-  if (direct_call == NULL)
+  count_all = all;
+  /* Do the indirect call conversion if function body exists, or else leave it
+     to LTO stage.  */
+  for (j = 1; j < histogram->n_counters; j += 2)
     {
-      if (val)
+      val = histogram->hvalue.counters[j];
+      count = histogram->hvalue.counters[j + 1];
+      if (val & count > 0)
 	{
-	  if (dump_file)
+	  /* The order of CHECK_COUNTER calls is important
+	     since check_counter can correct the third parameter
+	     and we want to make count <= all <= bb_count.  */
+	  if (check_counter (stmt, "ic", &all, &bb_all, bb_count)
+	      || check_counter (stmt, "ic", &count, &all,
+				profile_count::from_gcov_type (all),
+				(float) count / count_all))
 	    {
-	      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);
+	      gimple_remove_histogram_value (cfun, stmt, histogram);
+	      return false;
 	    }
-	}
-      return false;
-    }
 
-  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 (4 * count <= all)
+	    continue;
 
-  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);
+	  direct_call = find_func_by_profile_id ((int) val);
+
+	  if (direct_call == NULL)
+	    {
+	      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 (!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 (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 postponed 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;
-- 
2.22.0.428.g6d5b264208

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v2] Missed function specialization + partial devirtualization
  2019-07-12  9:19 [PATCH v2] Missed function specialization + partial devirtualization Xiong Hu Luo
@ 2019-07-15  8:20 ` Martin Liška
  2019-07-31  5:51   ` [PATCH v3] " luoxhu
  0 siblings, 1 reply; 38+ messages in thread
From: Martin Liška @ 2019-07-15  8:20 UTC (permalink / raw)
  To: Xiong Hu Luo, gcc-patches; +Cc: hubicka, segher, wschmidt

On 7/12/19 10:51 AM, Xiong Hu Luo wrote:
>  2. Remove duplicate logic in ipa-profile.  As
>  get_most_common_single_value could only return single value, but this
>  multiple indirect call needs store each hist value, will
>  consider specialize it later.

Hi.

I would like to see this patch rebased on the suggested patch that will
change get_most_common_value.

Thanks,
Martin

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH v3] Missed function specialization + partial devirtualization
  2019-07-15  8:20 ` Martin Liška
@ 2019-07-31  5:51   ` luoxhu
  2019-08-13 13:16     ` Martin Liška
  0 siblings, 1 reply; 38+ messages in thread
From: luoxhu @ 2019-07-31  5:51 UTC (permalink / raw)
  To: Martin Liška, gcc-patches; +Cc: hubicka, segher, wschmidt

This patch aims to fix PR69678 caused by PGO indirect call profiling
performance issues.
The bug that profiling data is never working was fixed by Martin's pull
back of topN patches, performance got GEOMEAN ~1% improvement.
Still, currently the default profile only generates SINGLE indirect target
that called more than 75%.  This patch leverages MULTIPLE indirect
targets use in LTO-WPA and LTO-LTRANS stage, as a result, function
specialization, profiling, partial devirtualization, inlining and
cloning could be done successfully based on it.
Performance can get improved from 0.70 sec to 0.38 sec on simple tests.
Details are:
  1.  PGO with topn is enbaled by default now, but only one indirect
  target edge will be generated in ipa-profile pass, so add variables to enable
  multiple speculative edges through passes, speculative_id will record the direct edge
  index bind to the indirect edge, num_of_ics records how many direct edges owned by
  the indirect edge, postpone gimple_ic to ipa-profile like default as inline
  pass will decide whether it is benefit to transform indirect call.
  2.  Enable LTO WPA/LTRANS stage multiple indirect call targets analysis for
  profile full support in ipa passes and cgraph_edge functions.  speculative_id
  can be set by make_speculative id when multiple targets are binded to
  one indirect edge, and cloned if new edge is cloned.  speculative_id
  is streamed out and stream int by lto like lto_stmt_uid.
  3.  Add 1 in module testcase and 2 cross module testcases.
  4.  Bootstrap and regression test passed on Power8-LE.

v3 Changes:
 1. Rebase to trunk.
 2. Use speculative_id to track and search the reference node matched
 with the direct edge's callee for multiple targets.  This could
 eliminate the workaround strstr before.  Actually, it is the caller's
 response to handle the direct edges mapped to same indirect edge.
 speculative_call_info will still return one of the direct edge
 specified, this will leverage current IPA edge process framework mostly.

gcc/ChangeLog

	2019-07-31  Xiong Hu Luo  <luoxhu@linux.ibm.com>

	PR ipa/69678
	* cgraph.c (symbol_table::create_edge): Init speculative_id.
	(cgraph_edge::make_speculative): Add param for setting speculative_id.
	(cgraph_edge::speculative_call_info): Find reference by
	speculative_id for multiple indirect targets.
	(cgraph_edge::resolve_speculation): Decrease the speculations
	for indirect edge, drop it's speculative if not direct target
	left.
	(cgraph_edge::redirect_call_stmt_to_callee): Likewise.
	(cgraph_node::verify_node): Don't report error if speculative
	edge not include statement.
	* cgraph.h (struct indirect_target_info): New struct.
	(indirect_call_targets): New vector variable.
	(num_of_ics): New variable.
	(make_speculative): Add param for setting speculative_id.
	(speculative_id): New variable.
	* cgraphclones.c (cgraph_node::create_clone): Clone speculative_id.
	* ipa-inline.c (inline_small_functions): Add iterator update.
	* ipa-profile.c (ipa_profile_generate_summary): Add indirect
	multiple targets logic.
	(ipa_profile): Likewise.
	* ipa-ref.h (speculative_id): New variable.
	* ipa.c (process_references): Fix typo.
	* lto-cgraph.c (lto_output_edge): Add indirect multiple targets
	logic.  Stream out speculative_id.
	(input_edge): Likewise.
	* predict.c (dump_prediction): Revome edges count assert to be
	precise.
	* symtab.c (symtab_node::create_reference): Init speculative_id.
	(symtab_node::clone_references): Clone speculative_id.
	(symtab_node::clone_referring): Clone speculative_id.
	(symtab_node::clone_reference): Clone speculative_id.
	(symtab_node::clear_stmts_in_references): Clear speculative_id.
	* tree-inline.c (copy_bb): Duplicate all the speculative edges
	if indirect call contains multiple speculative targets.
	* tree-profile.c (gimple_gen_ic_profiler): Use the new variable
	__gcov_indirect_call.counters and __gcov_indirect_call.callee.
	(gimple_gen_ic_func_profiler): Likewise.
	(pass_ipa_tree_profile::gate): Fix comment typos.
	* value-prof.c  (gimple_ic_transform): Handle topn case.
	Fix comment typos.

gcc/testsuite/ChangeLog

	2019-07-31  Xiong Hu Luo  <luoxhu@linux.ibm.com>

	PR ipa/69678
	* gcc.dg/tree-prof/indir-call-prof-topn.c: New testcase.
	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c: New testcase.
	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c: New testcase.
	* gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c: New testcase.
---
 gcc/cgraph.c                                  |  70 +++++-
 gcc/cgraph.h                                  |  28 ++-
 gcc/cgraphclones.c                            |   1 +
 gcc/ipa-inline.c                              |  15 +-
 gcc/ipa-profile.c                             | 238 +++++++++++-------
 gcc/ipa-ref.h                                 |   1 +
 gcc/ipa.c                                     |   2 +-
 gcc/lto-cgraph.c                              |  54 +++-
 gcc/predict.c                                 |   1 -
 gcc/symtab.c                                  |   5 +
 .../tree-prof/crossmodule-indir-call-topn-1.c |  35 +++
 .../crossmodule-indir-call-topn-1a.c          |  22 ++
 .../tree-prof/crossmodule-indir-call-topn-2.c |  42 ++++
 .../gcc.dg/tree-prof/indir-call-prof-topn.c   |  38 +++
 gcc/tree-inline.c                             |  20 ++
 gcc/tree-profile.c                            |  12 +-
 gcc/value-prof.c                              |  80 +++---
 17 files changed, 500 insertions(+), 164 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c

diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index a7e3bcf2132..d7be9e9c545 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -869,6 +869,7 @@ symbol_table::create_edge (cgraph_node *caller, cgraph_node *callee,
   edge->prev_callee = NULL;
   edge->next_callee = NULL;
   edge->lto_stmt_uid = 0;
+  edge->speculative_id = 0;
 
   edge->count = count;
 
@@ -1063,7 +1064,8 @@ cgraph_edge::remove (void)
    Return direct edge created.  */
 
 cgraph_edge *
-cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
+cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count,
+			       unsigned int speculative_id)
 {
   cgraph_node *n = caller;
   ipa_ref *ref = NULL;
@@ -1081,11 +1083,13 @@ cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
   else
     e2->can_throw_external = can_throw_external;
   e2->lto_stmt_uid = lto_stmt_uid;
+  e2->speculative_id = speculative_id;
   e2->in_polymorphic_cdtor = in_polymorphic_cdtor;
   count -= e2->count;
   symtab->call_edge_duplication_hooks (this, e2);
   ref = n->create_reference (n2, IPA_REF_ADDR, call_stmt);
   ref->lto_stmt_uid = lto_stmt_uid;
+  ref->speculative_id = speculative_id;
   ref->speculative = speculative;
   n2->mark_address_taken ();
   return e2;
@@ -1099,6 +1103,38 @@ cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
    call) and if one of them exists, all of them must exist.
 
    Given speculative call edge, return all three components.
+
+   For some indirect edge, it may maps to multiple direct edges, i.e. 1:N.
+   check the speculative_id to return all the three components for specified
+   direct edge or indirect edge.
+   If input is indirect, caller of this function will get the direct edge one by
+   one, get_edge will just return one of the direct edge mapped to the indirect
+   edge, the returned direct edge will be resolved or redirected by the caller,
+   then num_of_ics (speculations) is deceased in each access.
+   If input is direct, this function will get the indirect edge and reference
+   with matched speculative_id, the returned edge will also be resolved or
+   redirected, decrease the speculations accordingly.
+   Speculations of indirect edge will be dropped only if all direct edges
+   be handled.
+
+   e.g. for indirect edge E statement "call call_dest":
+
+   Redirect N3 after redirected N2:
+
+   if (call_dest == N2)
+     n2 ();
+   else if (call_dest == N3)
+     n3 ();
+   else
+     call call_dest
+
+   Resolve N3 and only redirect N2:
+
+   if (call_dest == N2)
+     n2 ();
+   else
+     call call_dest
+
  */
 
 void
@@ -1138,7 +1174,7 @@ cgraph_edge::speculative_call_info (cgraph_edge *&direct,
 
   reference = NULL;
   for (i = 0; e->caller->iterate_reference (i, ref); i++)
-    if (ref->speculative
+    if (ref->speculative && ref->speculative_id == e->speculative_id
 	&& ((ref->stmt && ref->stmt == e->call_stmt)
 	    || (!ref->stmt && ref->lto_stmt_uid == e->lto_stmt_uid)))
       {
@@ -1199,7 +1235,19 @@ cgraph_edge::resolve_speculation (tree callee_decl)
          in the functions inlined through it.  */
     }
   edge->count += e2->count;
-  edge->speculative = false;
+  /* edge is indirect, e2 is direct.  If edge contains multiple speculations,
+     remove one of speculations for this indirect edge, then if edge still
+     contains direct target, keep the speculation, next direct target
+     will continue use it.  Give up speculation completely if no direct
+     target is left for this indirect edge.  */
+  if (edge->indirect_info && edge->indirect_info->num_of_ics)
+    {
+      edge->indirect_info->num_of_ics--;
+      if (edge->indirect_info->num_of_ics == 0)
+	edge->speculative = false;
+    }
+  else
+    edge->speculative = false;
   e2->speculative = false;
   ref->remove_reference ();
   if (e2->indirect_unknown_callee || e2->inline_failed)
@@ -1333,7 +1381,19 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
 	  e->caller->set_call_stmt_including_clones (e->call_stmt, new_stmt,
 						     false);
 	  e->count = gimple_bb (e->call_stmt)->count;
-	  e2->speculative = false;
+	  /* edge is direct, e2 is indirect here.  If e2 contains multiple
+	     speculations, remove one of speculations for this indirect edge,
+	     then if e2 still contains direct target, keep the speculation,
+	     next direct target will continue use it.  Give up speculation
+	     completely if no direct target is left for this indirect e2.  */
+	  if (e2->indirect_info && e2->indirect_info->num_of_ics)
+	    {
+	      e2->indirect_info->num_of_ics--;
+	      if (e2->indirect_info->num_of_ics == 0)
+		e2->speculative = false;
+	    }
+	  else
+	    e2->speculative = false;
 	  e2->count = gimple_bb (e2->call_stmt)->count;
 	  ref->speculative = false;
 	  ref->stmt = NULL;
@@ -3434,7 +3494,7 @@ cgraph_node::verify_node (void)
 
       for (e = callees; e; e = e->next_callee)
 	{
-	  if (!e->aux)
+	  if (!e->aux && !e->speculative)
 	    {
 	      error ("edge %s->%s has no corresponding call_stmt",
 		     identifier_to_locale (e->caller->name ()),
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index fa5224fb3a5..0b453c3534c 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -1630,6 +1630,16 @@ private:
   void make_speculative (tree otr_type = NULL);
 };
 
+/* Structure containing indirect target information from profile.  */
+
+struct GTY (()) indirect_target_info
+{
+  /* Profile_id of common target obtained from profile.  */
+  unsigned int common_target_id;
+  /* Probability that call will land in function with COMMON_TARGET_ID.  */
+  int common_target_probability;
+};
+
 /* Structure containing additional information about an indirect call.  */
 
 class GTY(()) cgraph_indirect_call_info
@@ -1648,10 +1658,14 @@ public:
   int param_index;
   /* ECF flags determined from the caller.  */
   int ecf_flags;
-  /* Profile_id of common target obtrained from profile.  */
-  int common_target_id;
-  /* Probability that call will land in function with COMMON_TARGET_ID.  */
-  int common_target_probability;
+
+  /* An indirect call may contain one or multiple call targets.  */
+  vec<indirect_target_info, va_gc> *indirect_call_targets;
+
+  /* Number of indirect targets (speculations) for the indirect call.
+     Increased by ipa-profile, decreased by resolve_speculation or redirect
+     statement to callee.  */
+  unsigned num_of_ics;
 
   /* Set when the call is a virtual call with the parameter being the
      associated object pointer rather than a simple direct call.  */
@@ -1708,7 +1722,8 @@ public:
   /* Turn edge into speculative call calling N2. Update
      the profile so the direct call is taken COUNT times
      with FREQUENCY.  */
-  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count);
+  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count,
+				 unsigned int speculative_id = 0);
 
    /* Given speculative call edge, return all three components.  */
   void speculative_call_info (cgraph_edge *&direct, cgraph_edge *&indirect,
@@ -1786,6 +1801,9 @@ public:
   /* The stmt_uid of call_stmt.  This is used by LTO to recover the call_stmt
      when the function is serialized in.  */
   unsigned int lto_stmt_uid;
+  /* speculative id is used by multiple indirect targets when the function is
+   speculated.  */
+  unsigned int speculative_id;
   /* Whether this edge was made direct by indirect inlining.  */
   unsigned int indirect_inlining_edge : 1;
   /* Whether this edge describes an indirect call with an undetermined
diff --git a/gcc/cgraphclones.c b/gcc/cgraphclones.c
index fd867ecac91..ab6b9dd86c8 100644
--- a/gcc/cgraphclones.c
+++ b/gcc/cgraphclones.c
@@ -128,6 +128,7 @@ cgraph_edge::clone (cgraph_node *n, gcall *call_stmt, unsigned stmt_uid,
   new_edge->inline_failed = inline_failed;
   new_edge->indirect_inlining_edge = indirect_inlining_edge;
   new_edge->lto_stmt_uid = stmt_uid;
+  new_edge->speculative_id = speculative_id;
   /* Clone flags that depend on call_stmt availability manually.  */
   new_edge->can_throw_external = can_throw_external;
   new_edge->call_stmt_cannot_inline_p = call_stmt_cannot_inline_p;
diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
index 939d86ef94a..f5654198877 100644
--- a/gcc/ipa-inline.c
+++ b/gcc/ipa-inline.c
@@ -1877,12 +1877,15 @@ inline_small_functions (void)
 	}
       if (has_speculative)
 	for (edge = node->callees; edge; edge = next)
-	  if (edge->speculative && !speculation_useful_p (edge,
-							  edge->aux != NULL))
-	    {
-	      edge->resolve_speculation ();
-	      update = true;
-	    }
+	  {
+	    next = edge->next_callee;
+	    if (edge->speculative
+		&& !speculation_useful_p (edge, edge->aux != NULL))
+	      {
+		edge->resolve_speculation ();
+		update = true;
+	      }
+	  }
       if (update)
 	{
 	  struct cgraph_node *where = node->global.inlined_to
diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
index 970dba39c80..26214eba27e 100644
--- a/gcc/ipa-profile.c
+++ b/gcc/ipa-profile.c
@@ -192,23 +192,39 @@ ipa_profile_generate_summary (void)
 		  if (h)
 		    {
 		      gcov_type val, count, all;
-		      if (get_nth_most_common_value (NULL, "indirect call", h,
-						     &val, &count, &all))
+		      struct cgraph_edge *e = node->get_edge (stmt);
+		      if (e && !e->indirect_unknown_callee)
+			continue;
+
+		      e->indirect_info->num_of_ics = 0;
+		      for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
 			{
-			  struct cgraph_edge * e = node->get_edge (stmt);
-			  if (e && !e->indirect_unknown_callee)
+			  if (!get_nth_most_common_value (NULL, "indirect call",
+							  h, &val, &count, &all,
+							  j))
+			    continue;
+
+			  if (val == 0)
 			    continue;
 
-			  e->indirect_info->common_target_id = val;
-			  e->indirect_info->common_target_probability
+			  struct indirect_target_info item;
+			  item.common_target_id = val;
+			  item.common_target_probability
 			    = GCOV_COMPUTE_SCALE (count, all);
-			  if (e->indirect_info->common_target_probability > REG_BR_PROB_BASE)
+			  if (item.common_target_probability > REG_BR_PROB_BASE)
 			    {
 			      if (dump_file)
-				fprintf (dump_file, "Probability capped to 1\n");
-			      e->indirect_info->common_target_probability = REG_BR_PROB_BASE;
+				fprintf (dump_file,
+					 "Probability capped to 1\n");
+			      item.common_target_probability = REG_BR_PROB_BASE;
 			    }
+			  vec_safe_push (
+			    e->indirect_info->indirect_call_targets, item);
+			  e->indirect_info->num_of_ics++;
 			}
+		      gcc_assert (e->indirect_info->num_of_ics
+				  <= GCOV_TOPN_VALUES);
+
 		      gimple_remove_histogram_value (DECL_STRUCT_FUNCTION (node->decl),
 						      stmt, h);
 		    }
@@ -492,6 +508,7 @@ ipa_profile (void)
   int nindirect = 0, ncommon = 0, nunknown = 0, nuseless = 0, nconverted = 0;
   int nmismatch = 0, nimpossible = 0;
   bool node_map_initialized = false;
+  gcov_type threshold;
 
   if (dump_file)
     dump_histogram (dump_file, histogram);
@@ -500,14 +517,12 @@ ipa_profile (void)
       overall_time += histogram[i]->count * histogram[i]->time;
       overall_size += histogram[i]->size;
     }
+  threshold = 0;
   if (overall_time)
     {
-      gcov_type threshold;
-
       gcc_assert (overall_size);
 
       cutoff = (overall_time * PARAM_VALUE (HOT_BB_COUNT_WS_PERMILLE) + 500) / 1000;
-      threshold = 0;
       for (i = 0; cumulated < cutoff; i++)
 	{
 	  cumulated += histogram[i]->count * histogram[i]->time;
@@ -543,7 +558,7 @@ ipa_profile (void)
   histogram.release ();
   histogram_pool.release ();
 
-  /* Produce speculative calls: we saved common traget from porfiling into
+  /* Produce speculative calls: we saved common target from profiling into
      e->common_target_id.  Now, at link time, we can look up corresponding
      function node and produce speculative call.  */
 
@@ -558,104 +573,133 @@ ipa_profile (void)
 	{
 	  if (n->count.initialized_p ())
 	    nindirect++;
-	  if (e->indirect_info->common_target_id)
+	  if (e->indirect_info && e->indirect_info->num_of_ics)
 	    {
-	      if (!node_map_initialized)
-	        init_node_map (false);
-	      node_map_initialized = true;
-	      ncommon++;
-	      n2 = find_func_by_profile_id (e->indirect_info->common_target_id);
-	      if (n2)
+	      if (in_lto_p)
 		{
 		  if (dump_file)
 		    {
-		      fprintf (dump_file, "Indirect call -> direct call from"
-			       " other module %s => %s, prob %3.2f\n",
-			       n->dump_name (),
-			       n2->dump_name (),
-			       e->indirect_info->common_target_probability
-			       / (float)REG_BR_PROB_BASE);
-		    }
-		  if (e->indirect_info->common_target_probability
-		      < REG_BR_PROB_BASE / 2)
-		    {
-		      nuseless++;
-		      if (dump_file)
-			fprintf (dump_file,
-				 "Not speculating: probability is too low.\n");
-		    }
-		  else if (!e->maybe_hot_p ())
-		    {
-		      nuseless++;
-		      if (dump_file)
-			fprintf (dump_file,
-				 "Not speculating: call is cold.\n");
-		    }
-		  else if (n2->get_availability () <= AVAIL_INTERPOSABLE
-			   && n2->can_be_discarded_p ())
-		    {
-		      nuseless++;
-		      if (dump_file)
-			fprintf (dump_file,
-				 "Not speculating: target is overwritable "
-				 "and can be discarded.\n");
+		      fprintf (dump_file,
+			       "Updating hotness threshold in LTO mode.\n");
+		      fprintf (dump_file, "Updated min count: %" PRId64 "\n",
+			       (int64_t) threshold);
 		    }
-		  else if (ipa_node_params_sum && ipa_edge_args_sum
-			   && (!vec_safe_is_empty
-			       (IPA_NODE_REF (n2)->descriptors))
-			   && ipa_get_param_count (IPA_NODE_REF (n2))
-			      != ipa_get_cs_argument_count (IPA_EDGE_REF (e))
-			    && (ipa_get_param_count (IPA_NODE_REF (n2))
-				>= ipa_get_cs_argument_count (IPA_EDGE_REF (e))
-				|| !stdarg_p (TREE_TYPE (n2->decl))))
+		  set_hot_bb_threshold (threshold
+					/ e->indirect_info->num_of_ics);
+		}
+	      if (!node_map_initialized)
+		init_node_map (false);
+	      node_map_initialized = true;
+	      ncommon++;
+	      unsigned speculative_id = 0;
+	      struct indirect_target_info *item;
+	      FOR_EACH_VEC_SAFE_ELT (e->indirect_info->indirect_call_targets, i,
+				     item)
+		{
+		  if ((unsigned) i > e->indirect_info->num_of_ics)
+		    break;
+		  n2 = find_func_by_profile_id (item->common_target_id);
+		  if (n2)
 		    {
-		      nmismatch++;
 		      if (dump_file)
-			fprintf (dump_file,
-				 "Not speculating: "
-				 "parameter count mistmatch\n");
+			{
+			  fprintf (dump_file,
+				   "Indirect call -> direct call from"
+				   " other module %s => %s, prob %3.2f\n",
+				   n->dump_name (), n2->dump_name (),
+				   item->common_target_probability
+				     / (float) REG_BR_PROB_BASE);
+			}
+		      if (item->common_target_probability
+			  < REG_BR_PROB_BASE / 2)
+			{
+			  nuseless++;
+			  if (dump_file)
+			    fprintf (
+			      dump_file,
+			      "Not speculating: probability is too low.\n");
+			}
+		      else if (!e->maybe_hot_p ())
+			{
+			  nuseless++;
+			  if (dump_file)
+			    fprintf (dump_file,
+				     "Not speculating: call is cold.\n");
+			}
+		      else if (n2->get_availability () <= AVAIL_INTERPOSABLE
+			       && n2->can_be_discarded_p ())
+			{
+			  nuseless++;
+			  if (dump_file)
+			    fprintf (dump_file,
+				     "Not speculating: target is overwritable "
+				     "and can be discarded.\n");
+			}
+		      else if (ipa_node_params_sum && ipa_edge_args_sum
+			       && (!vec_safe_is_empty (
+				 IPA_NODE_REF (n2)->descriptors))
+			       && ipa_get_param_count (IPA_NODE_REF (n2))
+				    != ipa_get_cs_argument_count (
+				      IPA_EDGE_REF (e))
+			       && (ipa_get_param_count (IPA_NODE_REF (n2))
+				     >= ipa_get_cs_argument_count (
+				       IPA_EDGE_REF (e))
+				   || !stdarg_p (TREE_TYPE (n2->decl))))
+			{
+			  nmismatch++;
+			  if (dump_file)
+			    fprintf (dump_file, "Not speculating: "
+						"parameter count mismatch\n");
+			}
+		      else if (e->indirect_info->polymorphic
+			       && !opt_for_fn (n->decl, flag_devirtualize)
+			       && !possible_polymorphic_call_target_p (e, n2))
+			{
+			  nimpossible++;
+			  if (dump_file)
+			    fprintf (dump_file,
+				     "Not speculating: "
+				     "function is not in the polymorphic "
+				     "call target list\n");
+			}
+		      else
+			{
+			  /* Target may be overwritable, but profile says that
+			     control flow goes to this particular implementation
+			     of N2.  Speculate on the local alias to allow
+			     inlining.
+			     */
+			  if (!n2->can_be_discarded_p ())
+			    {
+			      cgraph_node *alias;
+			      alias = dyn_cast<cgraph_node *> (
+				n2->noninterposable_alias ());
+			      if (alias)
+				n2 = alias;
+			    }
+			  nconverted++;
+			  e->make_speculative (
+			    n2,
+			    e->count.apply_probability (
+			      item->common_target_probability),
+			    speculative_id);
+			  update = true;
+			  speculative_id++;
+			}
 		    }
-		  else if (e->indirect_info->polymorphic
-			   && !opt_for_fn (n->decl, flag_devirtualize)
-			   && !possible_polymorphic_call_target_p (e, n2))
+		  else
 		    {
-		      nimpossible++;
 		      if (dump_file)
 			fprintf (dump_file,
-				 "Not speculating: "
-				 "function is not in the polymorphic "
-				 "call target list\n");
+				 "Function with profile-id %i not found.\n",
+				 item->common_target_id);
+		      nunknown++;
 		    }
-		  else
-		    {
-		      /* Target may be overwritable, but profile says that
-			 control flow goes to this particular implementation
-			 of N2.  Speculate on the local alias to allow inlining.
-		       */
-		      if (!n2->can_be_discarded_p ())
-			{
-			  cgraph_node *alias;
-			  alias = dyn_cast<cgraph_node *> (n2->noninterposable_alias ());
-			  if (alias)
-			    n2 = alias;
-			}
-		      nconverted++;
-		      e->make_speculative
-			(n2,
-			 e->count.apply_probability
-				     (e->indirect_info->common_target_probability));
-		      update = true;
-		    }
-		}
-	      else
-		{
-		  if (dump_file)
-		    fprintf (dump_file, "Function with profile-id %i not found.\n",
-			     e->indirect_info->common_target_id);
-		  nunknown++;
 		}
+	      if (speculative_id < e->indirect_info->num_of_ics)
+		e->indirect_info->num_of_ics = speculative_id;
 	    }
-	 }
+	}
        if (update)
 	 ipa_update_overall_fn_summary (n);
      }
diff --git a/gcc/ipa-ref.h b/gcc/ipa-ref.h
index 0d8e509c932..3e6562ec9d1 100644
--- a/gcc/ipa-ref.h
+++ b/gcc/ipa-ref.h
@@ -59,6 +59,7 @@ public:
   symtab_node *referred;
   gimple *stmt;
   unsigned int lto_stmt_uid;
+  unsigned int speculative_id;
   unsigned int referred_index;
   ENUM_BITFIELD (ipa_ref_use) use:3;
   unsigned int speculative:1;
diff --git a/gcc/ipa.c b/gcc/ipa.c
index 2496694124c..c1fe081a72d 100644
--- a/gcc/ipa.c
+++ b/gcc/ipa.c
@@ -166,7 +166,7 @@ process_references (symtab_node *snode,
    devirtualization happens.  After inlining still keep their declarations
    around, so we can devirtualize to a direct call.
 
-   Also try to make trivial devirutalization when no or only one target is
+   Also try to make trivial devirtualization when no or only one target is
    possible.  */
 
 static void
diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
index bc0f0107333..1dc856d3c64 100644
--- a/gcc/lto-cgraph.c
+++ b/gcc/lto-cgraph.c
@@ -238,6 +238,7 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
   unsigned int uid;
   intptr_t ref;
   struct bitpack_d bp;
+  unsigned len;
 
   if (edge->indirect_unknown_callee)
     streamer_write_enum (ob->main_stream, LTO_symtab_tags, LTO_symtab_last_tag,
@@ -265,6 +266,7 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
   bp_pack_enum (&bp, cgraph_inline_failed_t,
 	        CIF_N_REASONS, edge->inline_failed);
   bp_pack_var_len_unsigned (&bp, uid);
+  bp_pack_var_len_unsigned (&bp, edge->speculative_id);
   bp_pack_value (&bp, edge->indirect_inlining_edge, 1);
   bp_pack_value (&bp, edge->speculative, 1);
   bp_pack_value (&bp, edge->call_stmt_cannot_inline_p, 1);
@@ -291,11 +293,28 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
   streamer_write_bitpack (&bp);
   if (edge->indirect_unknown_callee)
     {
+      struct indirect_target_info *item;
+      unsigned int i;
+      len = edge->indirect_info->num_of_ics;
+      gcc_assert (len <= GCOV_TOPN_VALUES);
+
       streamer_write_hwi_stream (ob->main_stream,
-			         edge->indirect_info->common_target_id);
-      if (edge->indirect_info->common_target_id)
-	streamer_write_hwi_stream
-	   (ob->main_stream, edge->indirect_info->common_target_probability);
+				 edge->indirect_info->num_of_ics);
+
+      if (len)
+	{
+	  FOR_EACH_VEC_SAFE_ELT (edge->indirect_info->indirect_call_targets, i,
+				 item)
+	    {
+	      if (i == edge->indirect_info->num_of_ics)
+		break;
+	      streamer_write_hwi_stream (ob->main_stream,
+					 item->common_target_id);
+	      if (item->common_target_id)
+		streamer_write_hwi_stream (ob->main_stream,
+					   item->common_target_probability);
+	    }
+	}
     }
 }
 
@@ -688,6 +707,7 @@ lto_output_ref (struct lto_simple_output_block *ob, struct ipa_ref *ref,
       if (ref->stmt)
 	uid = gimple_uid (ref->stmt) + 1;
       streamer_write_hwi_stream (ob->main_stream, uid);
+      streamer_write_hwi_stream (ob->main_stream, ref->speculative_id);
     }
 }
 
@@ -1420,6 +1440,8 @@ input_ref (class lto_input_block *ib,
   ref->speculative = speculative;
   if (is_a <cgraph_node *> (referring_node))
     ref->lto_stmt_uid = streamer_read_hwi (ib);
+  if (is_a <cgraph_node *> (referring_node))
+    ref->speculative_id = streamer_read_hwi (ib);
 }
 
 /* Read an edge from IB.  NODES points to a vector of previously read nodes for
@@ -1433,11 +1455,12 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
 {
   struct cgraph_node *caller, *callee;
   struct cgraph_edge *edge;
-  unsigned int stmt_id;
+  unsigned int stmt_id, speculative_id;
   profile_count count;
   cgraph_inline_failed_t inline_failed;
   struct bitpack_d bp;
   int ecf_flags = 0;
+  unsigned i, len;
 
   caller = dyn_cast<cgraph_node *> (nodes[streamer_read_hwi (ib)]);
   if (caller == NULL || caller->decl == NULL_TREE)
@@ -1457,6 +1480,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
   bp = streamer_read_bitpack (ib);
   inline_failed = bp_unpack_enum (&bp, cgraph_inline_failed_t, CIF_N_REASONS);
   stmt_id = bp_unpack_var_len_unsigned (&bp);
+  speculative_id = bp_unpack_var_len_unsigned (&bp);
 
   if (indirect)
     edge = caller->create_indirect_edge (NULL, 0, count);
@@ -1466,6 +1490,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
   edge->indirect_inlining_edge = bp_unpack_value (&bp, 1);
   edge->speculative = bp_unpack_value (&bp, 1);
   edge->lto_stmt_uid = stmt_id;
+  edge->speculative_id = speculative_id;
   edge->inline_failed = inline_failed;
   edge->call_stmt_cannot_inline_p = bp_unpack_value (&bp, 1);
   edge->can_throw_external = bp_unpack_value (&bp, 1);
@@ -1485,9 +1510,22 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
       if (bp_unpack_value (&bp, 1))
 	ecf_flags |= ECF_RETURNS_TWICE;
       edge->indirect_info->ecf_flags = ecf_flags;
-      edge->indirect_info->common_target_id = streamer_read_hwi (ib);
-      if (edge->indirect_info->common_target_id)
-        edge->indirect_info->common_target_probability = streamer_read_hwi (ib);
+
+      len = streamer_read_hwi (ib);
+      edge->indirect_info->num_of_ics = len;
+
+      gcc_assert (len <= GCOV_TOPN_VALUES);
+
+      if (len)
+	{
+	  for (i = 0; i < len; i++)
+	    {
+	      indirect_target_info item;
+	      item.common_target_id = streamer_read_hwi (ib);
+	      item.common_target_probability = streamer_read_hwi (ib);
+	      vec_safe_push (edge->indirect_info->indirect_call_targets, item);
+	    }
+	}
     }
 }
 
diff --git a/gcc/predict.c b/gcc/predict.c
index 07f66aab7a3..67a859ea436 100644
--- a/gcc/predict.c
+++ b/gcc/predict.c
@@ -763,7 +763,6 @@ dump_prediction (FILE *file, enum br_predictor predictor, int probability,
       && bb->count.precise_p ()
       && reason == REASON_NONE)
     {
-      gcc_assert (e->count ().precise_p ());
       fprintf (file, ";;heuristics;%s;%" PRId64 ";%" PRId64 ";%.1f;\n",
 	       predictor_info[predictor].name,
 	       bb->count.to_gcov_type (), e->count ().to_gcov_type (),
diff --git a/gcc/symtab.c b/gcc/symtab.c
index b1589ea8a40..90a1f62d6a9 100644
--- a/gcc/symtab.c
+++ b/gcc/symtab.c
@@ -603,6 +603,7 @@ symtab_node::create_reference (symtab_node *referred_node,
   ref->referred = referred_node;
   ref->stmt = stmt;
   ref->lto_stmt_uid = 0;
+  ref->speculative_id = 0;
   ref->use = use_type;
   ref->speculative = 0;
 
@@ -660,6 +661,7 @@ symtab_node::clone_references (symtab_node *node)
       ref2 = create_reference (ref->referred, ref->use, ref->stmt);
       ref2->speculative = speculative;
       ref2->lto_stmt_uid = stmt_uid;
+      ref2->speculative_id = ref->speculative_id;
     }
 }
 
@@ -678,6 +680,7 @@ symtab_node::clone_referring (symtab_node *node)
       ref2 = ref->referring->create_reference (this, ref->use, ref->stmt);
       ref2->speculative = speculative;
       ref2->lto_stmt_uid = stmt_uid;
+      ref2->speculative_id = ref->speculative_id;
     }
 }
 
@@ -693,6 +696,7 @@ symtab_node::clone_reference (ipa_ref *ref, gimple *stmt)
   ref2 = create_reference (ref->referred, ref->use, stmt);
   ref2->speculative = speculative;
   ref2->lto_stmt_uid = stmt_uid;
+  ref2->speculative_id = ref->speculative_id;
   return ref2;
 }
 
@@ -747,6 +751,7 @@ symtab_node::clear_stmts_in_references (void)
       {
 	r->stmt = NULL;
 	r->lto_stmt_uid = 0;
+	r->speculative_id = 0;
       }
 }
 
diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
new file mode 100644
index 00000000000..e0a83c2e067
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
@@ -0,0 +1,35 @@
+/* { dg-require-effective-target lto } */
+/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate --param indir-call-topn-profile=1" } */
+
+#include <stdio.h>
+
+typedef int (*fptr) (int);
+int
+one (int a);
+
+int
+two (int a);
+
+fptr table[] = {&one, &two};
+
+int
+main()
+{
+  int i, x;
+  fptr p = &one;
+
+  x = one (3);
+
+  for (i = 0; i < 350000000; i++)
+    {
+      x = (*p) (3);
+      p = table[x];
+    }
+  printf ("done:%d\n", x);
+}
+
+/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* one transformation on insn" "profile_estimate" } } */
+/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* two transformation on insn" "profile_estimate" } } */
+
diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
new file mode 100644
index 00000000000..a8c6e365fb9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
@@ -0,0 +1,22 @@
+/* It seems there is no way to avoid the other source of mulitple
+   source testcase from being compiled independently.  Just avoid
+   error.  */
+#ifdef DOJOB
+int
+one (int a)
+{
+  return 1;
+}
+
+int
+two (int a)
+{
+  return 0;
+}
+#else
+int
+main()
+{
+  return 0;
+}
+#endif
diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
new file mode 100644
index 00000000000..aa3887fde83
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
@@ -0,0 +1,42 @@
+/* { dg-require-effective-target lto } */
+/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate --param indir-call-topn-profile=1" } */
+
+#include <stdio.h>
+
+typedef int (*fptr) (int);
+int
+one (int a);
+
+int
+two (int a);
+
+fptr table[] = {&one, &two};
+
+int foo ()
+{
+  int i, x;
+  fptr p = &one;
+
+  x = one (3);
+
+  for (i = 0; i < 350000000; i++)
+    {
+      x = (*p) (3);
+      p = table[x];
+    }
+  return x;
+}
+
+int
+main()
+{
+  int x = foo ();
+  printf ("done:%d\n", x);
+}
+
+/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* one transformation on insn" "profile_estimate" } } */
+/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* two transformation on insn" "profile_estimate" } } */
+
+
diff --git a/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
new file mode 100644
index 00000000000..951bc7ddd19
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
@@ -0,0 +1,38 @@
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-options "-O2 -fdump-ipa-profile --param indir-call-topn-profile=1" } */
+
+#include <stdio.h>
+
+typedef int (*fptr) (int);
+int
+one (int a)
+{
+  return 1;
+}
+
+int
+two (int a)
+{
+  return 0;
+}
+
+fptr table[] = {&one, &two};
+
+int
+main()
+{
+  int i, x;
+  fptr p = &one;
+
+  one (3);
+
+  for (i = 0; i < 350000000; i++)
+    {
+      x = (*p) (3);
+      p = table[x];
+    }
+  printf ("done:%d\n", x);
+}
+
+/* { dg-final-use-not-autofdo { scan-ipa-dump "Indirect call -> direct call.* one transformation on insn" "profile" } } */
+/* { dg-final-use-not-autofdo { scan-ipa-dump "Indirect call -> direct call.* two transformation on insn" "profile" } } */
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index 4311309acce..93d9269aa8f 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -2167,6 +2167,26 @@ copy_bb (copy_body_data *id, basic_block bb,
 
 			  gcc_assert (!edge->indirect_unknown_callee);
 			  old_edge->speculative_call_info (direct, indirect, ref);
+			  while (old_edge->next_callee
+				 && old_edge->next_callee->speculative
+				 && indirect->indirect_info
+				 && indirect->indirect_info->num_of_ics > 1)
+			    {
+			      /* Some speculative calls may contain more than
+				 one direct target, loop iterate it to clone all
+				 related direct edges before cloning the related
+				 indirect edge.  */
+			      id->dst_node->clone_reference (ref, stmt);
+
+			      edge = old_edge->next_callee;
+			      edge = edge->clone (id->dst_node, call_stmt,
+						  gimple_uid (stmt), num, den,
+						  true);
+			      old_edge = old_edge->next_callee;
+			      gcc_assert (!edge->indirect_unknown_callee);
+			      old_edge->speculative_call_info (direct, indirect,
+							       ref);
+			    }
 
 			  profile_count indir_cnt = indirect->count;
 			  indirect = indirect->clone (id->dst_node, call_stmt,
diff --git a/gcc/tree-profile.c b/gcc/tree-profile.c
index 554a8c98419..f35bd3f9d7a 100644
--- a/gcc/tree-profile.c
+++ b/gcc/tree-profile.c
@@ -74,8 +74,8 @@ static GTY(()) tree ic_tuple_callee_field;
 /* Do initialization work for the edge profiler.  */
 
 /* Add code:
-   __thread gcov*	__gcov_indirect_call_counters; // pointer to actual counter
-   __thread void*	__gcov_indirect_call_callee; // actual callee address
+   __thread gcov*	__gcov_indirect_call.counters; // pointer to actual counter
+   __thread void*	__gcov_indirect_call.callee; // actual callee address
    __thread int __gcov_function_counter; // time profiler function counter
 */
 static void
@@ -384,7 +384,7 @@ gimple_gen_ic_profiler (histogram_value value, unsigned tag, unsigned base)
       f_1 = foo;
       __gcov_indirect_call.counters = &__gcov4.main[0];
       PROF_9 = f_1;
-      __gcov_indirect_call_callee = PROF_9;
+      __gcov_indirect_call.callee = PROF_9;
       _4 = f_1 ();
    */
 
@@ -447,11 +447,11 @@ gimple_gen_ic_func_profiler (void)
 
   /* Insert code:
 
-     if (__gcov_indirect_call_callee != NULL)
+     if (__gcov_indirect_call.callee != NULL)
        __gcov_indirect_call_profiler_v3 (profile_id, &current_function_decl);
 
      The function __gcov_indirect_call_profiler_v3 is responsible for
-     resetting __gcov_indirect_call_callee to NULL.  */
+     resetting __gcov_indirect_call.callee to NULL.  */
 
   gimple_stmt_iterator gsi = gsi_start_bb (cond_bb);
   void0 = build_int_cst (ptr_type_node, 0);
@@ -893,7 +893,7 @@ pass_ipa_tree_profile::gate (function *)
 {
   /* When profile instrumentation, use or test coverage shall be performed.
      But for AutoFDO, this there is no instrumentation, thus this pass is
-     diabled.  */
+     disabled.  */
   return (!in_lto_p && !flag_auto_profile
 	  && (flag_branch_probabilities || flag_test_coverage
 	      || profile_arc_flag));
diff --git a/gcc/value-prof.c b/gcc/value-prof.c
index 759458868a8..cbcc104bd5c 100644
--- a/gcc/value-prof.c
+++ b/gcc/value-prof.c
@@ -1404,11 +1404,10 @@ gimple_ic (gcall *icall_stmt, struct cgraph_node *direct_call,
   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:
- */
+/* There maybe multiple indirect targets in histogram.  Check every
+   indirect/virtual call if callee function exists, if not exist, leave it to
+   LTO stage for later process.  Modify code of this indirect call to an if-else
+   structure in ipa-profile finally.  */
 
 static bool
 gimple_ic_transform (gimple_stmt_iterator *gsi)
@@ -1432,54 +1431,65 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
   if (!histogram)
     return false;
 
-  if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
-				  &count, &all))
-    return false;
+  count = 0;
+  all = histogram->hvalue.counters[0];
 
-  if (4 * count <= 3 * all)
-    return false;
+  for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
+    {
+      if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
+				      &count, &all, j))
+	continue;
 
-  direct_call = find_func_by_profile_id ((int)val);
+      if (4 * count <= all)
+	continue;
 
-  if (direct_call == NULL)
-    {
-      if (val)
+      direct_call = find_func_by_profile_id ((int) val);
+
+      if (direct_call == NULL)
+	{
+	  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);
+		}
+	    }
+	  continue;
+	}
+
+      if (!check_ic_target (stmt, direct_call))
 	{
 	  if (dump_file)
 	    {
-	      fprintf (dump_file, "Indirect call -> direct call from other module");
+	      fprintf (dump_file, "Indirect call -> direct call ");
 	      print_generic_expr (dump_file, gimple_call_fn (stmt), TDF_SLIM);
-	      fprintf (dump_file, "=> %i (will resolve only with LTO)\n", (int)val);
+	      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;
 	}
-      return false;
-    }
 
-  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");
+	  fprintf (dump_file,
+		   " transformation on insn postponed to ipa-profile");
 	  print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
+	  fprintf (dump_file, "hist->count %" PRId64 " hist->all %" PRId64 "\n",
+		   count, all);
 	}
-      gimple_remove_histogram_value (cfun, stmt, histogram);
-      return false;
-    }
-
-  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;
-- 
2.22.0.428.g6d5b264208

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v3] Missed function specialization + partial devirtualization
  2019-07-31  5:51   ` [PATCH v3] " luoxhu
@ 2019-08-13 13:16     ` Martin Liška
  2019-08-13 13:21       ` Martin Liška
  2019-09-25  3:46       ` [PATCH v4] " luoxhu
  0 siblings, 2 replies; 38+ messages in thread
From: Martin Liška @ 2019-08-13 13:16 UTC (permalink / raw)
  To: luoxhu, gcc-patches; +Cc: hubicka, segher, wschmidt

On 7/31/19 7:29 AM, luoxhu wrote:
> This patch aims to fix PR69678 caused by PGO indirect call profiling
> performance issues.
> The bug that profiling data is never working was fixed by Martin's pull
> back of topN patches, performance got GEOMEAN ~1% improvement.
> Still, currently the default profile only generates SINGLE indirect target
> that called more than 75%.  This patch leverages MULTIPLE indirect
> targets use in LTO-WPA and LTO-LTRANS stage, as a result, function
> specialization, profiling, partial devirtualization, inlining and
> cloning could be done successfully based on it.
> Performance can get improved from 0.70 sec to 0.38 sec on simple tests.
> Details are:
>   1.  PGO with topn is enbaled by default now, but only one indirect
>   target edge will be generated in ipa-profile pass, so add variables to enable
>   multiple speculative edges through passes, speculative_id will record the direct edge
>   index bind to the indirect edge, num_of_ics records how many direct edges owned by
>   the indirect edge, postpone gimple_ic to ipa-profile like default as inline
>   pass will decide whether it is benefit to transform indirect call.
>   2.  Enable LTO WPA/LTRANS stage multiple indirect call targets analysis for
>   profile full support in ipa passes and cgraph_edge functions.  speculative_id
>   can be set by make_speculative id when multiple targets are binded to
>   one indirect edge, and cloned if new edge is cloned.  speculative_id
>   is streamed out and stream int by lto like lto_stmt_uid.
>   3.  Add 1 in module testcase and 2 cross module testcases.
>   4.  Bootstrap and regression test passed on Power8-LE.
> 
> v3 Changes:
>  1. Rebase to trunk.
>  2. Use speculative_id to track and search the reference node matched
>  with the direct edge's callee for multiple targets.  This could
>  eliminate the workaround strstr before.  Actually, it is the caller's
>  response to handle the direct edges mapped to same indirect edge.
>  speculative_call_info will still return one of the direct edge
>  specified, this will leverage current IPA edge process framework mostly.

Thank you for the next version of the patch. Inline comments follow:

> 
> gcc/ChangeLog
> 
> 	2019-07-31  Xiong Hu Luo  <luoxhu@linux.ibm.com>
> 
> 	PR ipa/69678
> 	* cgraph.c (symbol_table::create_edge): Init speculative_id.
> 	(cgraph_edge::make_speculative): Add param for setting speculative_id.
> 	(cgraph_edge::speculative_call_info): Find reference by
> 	speculative_id for multiple indirect targets.
> 	(cgraph_edge::resolve_speculation): Decrease the speculations
> 	for indirect edge, drop it's speculative if not direct target
> 	left.
> 	(cgraph_edge::redirect_call_stmt_to_callee): Likewise.
> 	(cgraph_node::verify_node): Don't report error if speculative
> 	edge not include statement.
> 	* cgraph.h (struct indirect_target_info): New struct.
> 	(indirect_call_targets): New vector variable.
> 	(num_of_ics): New variable.
> 	(make_speculative): Add param for setting speculative_id.
> 	(speculative_id): New variable.
> 	* cgraphclones.c (cgraph_node::create_clone): Clone speculative_id.
> 	* ipa-inline.c (inline_small_functions): Add iterator update.
> 	* ipa-profile.c (ipa_profile_generate_summary): Add indirect
> 	multiple targets logic.
> 	(ipa_profile): Likewise.
> 	* ipa-ref.h (speculative_id): New variable.
> 	* ipa.c (process_references): Fix typo.
> 	* lto-cgraph.c (lto_output_edge): Add indirect multiple targets
> 	logic.  Stream out speculative_id.
> 	(input_edge): Likewise.
> 	* predict.c (dump_prediction): Revome edges count assert to be
> 	precise.
> 	* symtab.c (symtab_node::create_reference): Init speculative_id.
> 	(symtab_node::clone_references): Clone speculative_id.
> 	(symtab_node::clone_referring): Clone speculative_id.
> 	(symtab_node::clone_reference): Clone speculative_id.
> 	(symtab_node::clear_stmts_in_references): Clear speculative_id.
> 	* tree-inline.c (copy_bb): Duplicate all the speculative edges
> 	if indirect call contains multiple speculative targets.
> 	* tree-profile.c (gimple_gen_ic_profiler): Use the new variable
> 	__gcov_indirect_call.counters and __gcov_indirect_call.callee.
> 	(gimple_gen_ic_func_profiler): Likewise.
> 	(pass_ipa_tree_profile::gate): Fix comment typos.
> 	* value-prof.c  (gimple_ic_transform): Handle topn case.
> 	Fix comment typos.
> 
> gcc/testsuite/ChangeLog
> 
> 	2019-07-31  Xiong Hu Luo  <luoxhu@linux.ibm.com>
> 
> 	PR ipa/69678
> 	* gcc.dg/tree-prof/indir-call-prof-topn.c: New testcase.
> 	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c: New testcase.
> 	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c: New testcase.
> 	* gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c: New testcase.
> ---
>  gcc/cgraph.c                                  |  70 +++++-
>  gcc/cgraph.h                                  |  28 ++-
>  gcc/cgraphclones.c                            |   1 +
>  gcc/ipa-inline.c                              |  15 +-
>  gcc/ipa-profile.c                             | 238 +++++++++++-------
>  gcc/ipa-ref.h                                 |   1 +
>  gcc/ipa.c                                     |   2 +-
>  gcc/lto-cgraph.c                              |  54 +++-
>  gcc/predict.c                                 |   1 -
>  gcc/symtab.c                                  |   5 +
>  .../tree-prof/crossmodule-indir-call-topn-1.c |  35 +++
>  .../crossmodule-indir-call-topn-1a.c          |  22 ++
>  .../tree-prof/crossmodule-indir-call-topn-2.c |  42 ++++
>  .../gcc.dg/tree-prof/indir-call-prof-topn.c   |  38 +++
>  gcc/tree-inline.c                             |  20 ++
>  gcc/tree-profile.c                            |  12 +-
>  gcc/value-prof.c                              |  80 +++---
>  17 files changed, 500 insertions(+), 164 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
>  create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
>  create mode 100644 gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
> 
> diff --git a/gcc/cgraph.c b/gcc/cgraph.c
> index a7e3bcf2132..d7be9e9c545 100644
> --- a/gcc/cgraph.c
> +++ b/gcc/cgraph.c
> @@ -869,6 +869,7 @@ symbol_table::create_edge (cgraph_node *caller, cgraph_node *callee,
>    edge->prev_callee = NULL;
>    edge->next_callee = NULL;
>    edge->lto_stmt_uid = 0;
> +  edge->speculative_id = 0;
>  
>    edge->count = count;
>  
> @@ -1063,7 +1064,8 @@ cgraph_edge::remove (void)
>     Return direct edge created.  */
>  
>  cgraph_edge *
> -cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
> +cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count,
> +			       unsigned int speculative_id)
>  {
>    cgraph_node *n = caller;
>    ipa_ref *ref = NULL;
> @@ -1081,11 +1083,13 @@ cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
>    else
>      e2->can_throw_external = can_throw_external;
>    e2->lto_stmt_uid = lto_stmt_uid;
> +  e2->speculative_id = speculative_id;
>    e2->in_polymorphic_cdtor = in_polymorphic_cdtor;
>    count -= e2->count;
>    symtab->call_edge_duplication_hooks (this, e2);
>    ref = n->create_reference (n2, IPA_REF_ADDR, call_stmt);
>    ref->lto_stmt_uid = lto_stmt_uid;
> +  ref->speculative_id = speculative_id;
>    ref->speculative = speculative;
>    n2->mark_address_taken ();
>    return e2;
> @@ -1099,6 +1103,38 @@ cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
>     call) and if one of them exists, all of them must exist.
>  
>     Given speculative call edge, return all three components.
> +
> +   For some indirect edge, it may maps to multiple direct edges, i.e. 1:N.
> +   check the speculative_id to return all the three components for specified
> +   direct edge or indirect edge.
> +   If input is indirect, caller of this function will get the direct edge one by
> +   one, get_edge will just return one of the direct edge mapped to the indirect
> +   edge, the returned direct edge will be resolved or redirected by the caller,
> +   then num_of_ics (speculations) is deceased in each access.
> +   If input is direct, this function will get the indirect edge and reference
> +   with matched speculative_id, the returned edge will also be resolved or
> +   redirected, decrease the speculations accordingly.
> +   Speculations of indirect edge will be dropped only if all direct edges
> +   be handled.
> +
> +   e.g. for indirect edge E statement "call call_dest":
> +
> +   Redirect N3 after redirected N2:
> +
> +   if (call_dest == N2)
> +     n2 ();
> +   else if (call_dest == N3)
> +     n3 ();
> +   else
> +     call call_dest
> +
> +   Resolve N3 and only redirect N2:
> +
> +   if (call_dest == N2)
> +     n2 ();
> +   else
> +     call call_dest
> +
>   */
>  
>  void
> @@ -1138,7 +1174,7 @@ cgraph_edge::speculative_call_info (cgraph_edge *&direct,
>  
>    reference = NULL;
>    for (i = 0; e->caller->iterate_reference (i, ref); i++)
> -    if (ref->speculative
> +    if (ref->speculative && ref->speculative_id == e->speculative_id
>  	&& ((ref->stmt && ref->stmt == e->call_stmt)
>  	    || (!ref->stmt && ref->lto_stmt_uid == e->lto_stmt_uid)))
>        {
> @@ -1199,7 +1235,19 @@ cgraph_edge::resolve_speculation (tree callee_decl)
>           in the functions inlined through it.  */
>      }
>    edge->count += e2->count;
> -  edge->speculative = false;
> +  /* edge is indirect, e2 is direct.  If edge contains multiple speculations,
> +     remove one of speculations for this indirect edge, then if edge still
> +     contains direct target, keep the speculation, next direct target
> +     will continue use it.  Give up speculation completely if no direct
> +     target is left for this indirect edge.  */
> +  if (edge->indirect_info && edge->indirect_info->num_of_ics)
> +    {
> +      edge->indirect_info->num_of_ics--;
> +      if (edge->indirect_info->num_of_ics == 0)
> +	edge->speculative = false;
> +    }
> +  else
> +    edge->speculative = false;
>    e2->speculative = false;
>    ref->remove_reference ();
>    if (e2->indirect_unknown_callee || e2->inline_failed)
> @@ -1333,7 +1381,19 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
>  	  e->caller->set_call_stmt_including_clones (e->call_stmt, new_stmt,
>  						     false);
>  	  e->count = gimple_bb (e->call_stmt)->count;
> -	  e2->speculative = false;
> +	  /* edge is direct, e2 is indirect here.  If e2 contains multiple
> +	     speculations, remove one of speculations for this indirect edge,
> +	     then if e2 still contains direct target, keep the speculation,
> +	     next direct target will continue use it.  Give up speculation
> +	     completely if no direct target is left for this indirect e2.  */
> +	  if (e2->indirect_info && e2->indirect_info->num_of_ics)
> +	    {
> +	      e2->indirect_info->num_of_ics--;
> +	      if (e2->indirect_info->num_of_ics == 0)
> +		e2->speculative = false;
> +	    }
> +	  else
> +	    e2->speculative = false;
>  	  e2->count = gimple_bb (e2->call_stmt)->count;
>  	  ref->speculative = false;
>  	  ref->stmt = NULL;
> @@ -3434,7 +3494,7 @@ cgraph_node::verify_node (void)
>  
>        for (e = callees; e; e = e->next_callee)
>  	{
> -	  if (!e->aux)
> +	  if (!e->aux && !e->speculative)
>  	    {
>  	      error ("edge %s->%s has no corresponding call_stmt",
>  		     identifier_to_locale (e->caller->name ()),
> diff --git a/gcc/cgraph.h b/gcc/cgraph.h
> index fa5224fb3a5..0b453c3534c 100644
> --- a/gcc/cgraph.h
> +++ b/gcc/cgraph.h
> @@ -1630,6 +1630,16 @@ private:
>    void make_speculative (tree otr_type = NULL);
>  };
>  
> +/* Structure containing indirect target information from profile.  */
> +
> +struct GTY (()) indirect_target_info
> +{
> +  /* Profile_id of common target obtained from profile.  */
> +  unsigned int common_target_id;
> +  /* Probability that call will land in function with COMMON_TARGET_ID.  */
> +  int common_target_probability;
> +};
> +
>  /* Structure containing additional information about an indirect call.  */
>  
>  class GTY(()) cgraph_indirect_call_info
> @@ -1648,10 +1658,14 @@ public:
>    int param_index;
>    /* ECF flags determined from the caller.  */
>    int ecf_flags;
> -  /* Profile_id of common target obtrained from profile.  */
> -  int common_target_id;
> -  /* Probability that call will land in function with COMMON_TARGET_ID.  */
> -  int common_target_probability;
> +
> +  /* An indirect call may contain one or multiple call targets.  */
> +  vec<indirect_target_info, va_gc> *indirect_call_targets;
> +
> +  /* Number of indirect targets (speculations) for the indirect call.
> +     Increased by ipa-profile, decreased by resolve_speculation or redirect
> +     statement to callee.  */
> +  unsigned num_of_ics;

Would it be possible to use just the vector and replace all usages of num_of_ics
with indirect_call_targets->length () ? It would make the code easier.

>  
>    /* Set when the call is a virtual call with the parameter being the
>       associated object pointer rather than a simple direct call.  */
> @@ -1708,7 +1722,8 @@ public:
>    /* Turn edge into speculative call calling N2. Update
>       the profile so the direct call is taken COUNT times
>       with FREQUENCY.  */
> -  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count);
> +  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count,
> +				 unsigned int speculative_id = 0);
>  
>     /* Given speculative call edge, return all three components.  */
>    void speculative_call_info (cgraph_edge *&direct, cgraph_edge *&indirect,
> @@ -1786,6 +1801,9 @@ public:
>    /* The stmt_uid of call_stmt.  This is used by LTO to recover the call_stmt
>       when the function is serialized in.  */
>    unsigned int lto_stmt_uid;
> +  /* speculative id is used by multiple indirect targets when the function is
> +   speculated.  */
> +  unsigned int speculative_id;
>    /* Whether this edge was made direct by indirect inlining.  */
>    unsigned int indirect_inlining_edge : 1;
>    /* Whether this edge describes an indirect call with an undetermined
> diff --git a/gcc/cgraphclones.c b/gcc/cgraphclones.c
> index fd867ecac91..ab6b9dd86c8 100644
> --- a/gcc/cgraphclones.c
> +++ b/gcc/cgraphclones.c
> @@ -128,6 +128,7 @@ cgraph_edge::clone (cgraph_node *n, gcall *call_stmt, unsigned stmt_uid,
>    new_edge->inline_failed = inline_failed;
>    new_edge->indirect_inlining_edge = indirect_inlining_edge;
>    new_edge->lto_stmt_uid = stmt_uid;
> +  new_edge->speculative_id = speculative_id;
>    /* Clone flags that depend on call_stmt availability manually.  */
>    new_edge->can_throw_external = can_throw_external;
>    new_edge->call_stmt_cannot_inline_p = call_stmt_cannot_inline_p;
> diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
> index 939d86ef94a..f5654198877 100644
> --- a/gcc/ipa-inline.c
> +++ b/gcc/ipa-inline.c
> @@ -1877,12 +1877,15 @@ inline_small_functions (void)
>  	}
>        if (has_speculative)
>  	for (edge = node->callees; edge; edge = next)
> -	  if (edge->speculative && !speculation_useful_p (edge,
> -							  edge->aux != NULL))
> -	    {
> -	      edge->resolve_speculation ();
> -	      update = true;
> -	    }
> +	  {
> +	    next = edge->next_callee;
> +	    if (edge->speculative
> +		&& !speculation_useful_p (edge, edge->aux != NULL))
> +	      {
> +		edge->resolve_speculation ();
> +		update = true;
> +	      }
> +	  }
>        if (update)
>  	{
>  	  struct cgraph_node *where = node->global.inlined_to
> diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
> index 970dba39c80..26214eba27e 100644
> --- a/gcc/ipa-profile.c
> +++ b/gcc/ipa-profile.c
> @@ -192,23 +192,39 @@ ipa_profile_generate_summary (void)
>  		  if (h)
>  		    {
>  		      gcov_type val, count, all;
> -		      if (get_nth_most_common_value (NULL, "indirect call", h,
> -						     &val, &count, &all))
> +		      struct cgraph_edge *e = node->get_edge (stmt);
> +		      if (e && !e->indirect_unknown_callee)
> +			continue;
> +
> +		      e->indirect_info->num_of_ics = 0;
> +		      for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
>  			{
> -			  struct cgraph_edge * e = node->get_edge (stmt);
> -			  if (e && !e->indirect_unknown_callee)
> +			  if (!get_nth_most_common_value (NULL, "indirect call",
> +							  h, &val, &count, &all,
> +							  j))
> +			    continue;
> +
> +			  if (val == 0)
>  			    continue;
>  
> -			  e->indirect_info->common_target_id = val;
> -			  e->indirect_info->common_target_probability
> +			  struct indirect_target_info item;
> +			  item.common_target_id = val;
> +			  item.common_target_probability
>  			    = GCOV_COMPUTE_SCALE (count, all);
> -			  if (e->indirect_info->common_target_probability > REG_BR_PROB_BASE)
> +			  if (item.common_target_probability > REG_BR_PROB_BASE)
>  			    {
>  			      if (dump_file)
> -				fprintf (dump_file, "Probability capped to 1\n");
> -			      e->indirect_info->common_target_probability = REG_BR_PROB_BASE;
> +				fprintf (dump_file,
> +					 "Probability capped to 1\n");
> +			      item.common_target_probability = REG_BR_PROB_BASE;
>  			    }
> +			  vec_safe_push (
> +			    e->indirect_info->indirect_call_targets, item);
> +			  e->indirect_info->num_of_ics++;
>  			}
> +		      gcc_assert (e->indirect_info->num_of_ics
> +				  <= GCOV_TOPN_VALUES);
> +
>  		      gimple_remove_histogram_value (DECL_STRUCT_FUNCTION (node->decl),
>  						      stmt, h);
>  		    }
> @@ -492,6 +508,7 @@ ipa_profile (void)
>    int nindirect = 0, ncommon = 0, nunknown = 0, nuseless = 0, nconverted = 0;
>    int nmismatch = 0, nimpossible = 0;
>    bool node_map_initialized = false;
> +  gcov_type threshold;
>  
>    if (dump_file)
>      dump_histogram (dump_file, histogram);
> @@ -500,14 +517,12 @@ ipa_profile (void)
>        overall_time += histogram[i]->count * histogram[i]->time;
>        overall_size += histogram[i]->size;
>      }
> +  threshold = 0;
>    if (overall_time)
>      {
> -      gcov_type threshold;
> -
>        gcc_assert (overall_size);
>  
>        cutoff = (overall_time * PARAM_VALUE (HOT_BB_COUNT_WS_PERMILLE) + 500) / 1000;
> -      threshold = 0;
>        for (i = 0; cumulated < cutoff; i++)
>  	{
>  	  cumulated += histogram[i]->count * histogram[i]->time;
> @@ -543,7 +558,7 @@ ipa_profile (void)
>    histogram.release ();
>    histogram_pool.release ();
>  
> -  /* Produce speculative calls: we saved common traget from porfiling into
> +  /* Produce speculative calls: we saved common target from profiling into
>       e->common_target_id.  Now, at link time, we can look up corresponding
>       function node and produce speculative call.  */
>  
> @@ -558,104 +573,133 @@ ipa_profile (void)
>  	{
>  	  if (n->count.initialized_p ())
>  	    nindirect++;
> -	  if (e->indirect_info->common_target_id)
> +	  if (e->indirect_info && e->indirect_info->num_of_ics)
>  	    {
> -	      if (!node_map_initialized)
> -	        init_node_map (false);
> -	      node_map_initialized = true;
> -	      ncommon++;
> -	      n2 = find_func_by_profile_id (e->indirect_info->common_target_id);
> -	      if (n2)
> +	      if (in_lto_p)
>  		{

Starting from here, it's very difficult to read the patch. Reason is that you
wrapped all this in a FOR_EACH_VEC_SAFE_ELT. Would it be please possible
to make a refactoring patch that will put code with all the if-else-else-else..
into a separate function? Then you'll be able to call the function in a small loop:

FOR_EACH_VEC_SAFE_ELT (e->indirect_info->indirect_call_targets, i, item)
   new_function_call...

It will simplify the patch review a lot.


>  		  if (dump_file)
>  		    {
> -		      fprintf (dump_file, "Indirect call -> direct call from"
> -			       " other module %s => %s, prob %3.2f\n",
> -			       n->dump_name (),
> -			       n2->dump_name (),
> -			       e->indirect_info->common_target_probability
> -			       / (float)REG_BR_PROB_BASE);
> -		    }
> -		  if (e->indirect_info->common_target_probability
> -		      < REG_BR_PROB_BASE / 2)
> -		    {
> -		      nuseless++;
> -		      if (dump_file)
> -			fprintf (dump_file,
> -				 "Not speculating: probability is too low.\n");
> -		    }
> -		  else if (!e->maybe_hot_p ())
> -		    {
> -		      nuseless++;
> -		      if (dump_file)
> -			fprintf (dump_file,
> -				 "Not speculating: call is cold.\n");
> -		    }
> -		  else if (n2->get_availability () <= AVAIL_INTERPOSABLE
> -			   && n2->can_be_discarded_p ())
> -		    {
> -		      nuseless++;
> -		      if (dump_file)
> -			fprintf (dump_file,
> -				 "Not speculating: target is overwritable "
> -				 "and can be discarded.\n");
> +		      fprintf (dump_file,
> +			       "Updating hotness threshold in LTO mode.\n");
> +		      fprintf (dump_file, "Updated min count: %" PRId64 "\n",
> +			       (int64_t) threshold);
>  		    }
> -		  else if (ipa_node_params_sum && ipa_edge_args_sum
> -			   && (!vec_safe_is_empty
> -			       (IPA_NODE_REF (n2)->descriptors))
> -			   && ipa_get_param_count (IPA_NODE_REF (n2))
> -			      != ipa_get_cs_argument_count (IPA_EDGE_REF (e))
> -			    && (ipa_get_param_count (IPA_NODE_REF (n2))
> -				>= ipa_get_cs_argument_count (IPA_EDGE_REF (e))
> -				|| !stdarg_p (TREE_TYPE (n2->decl))))
> +		  set_hot_bb_threshold (threshold
> +					/ e->indirect_info->num_of_ics);
> +		}
> +	      if (!node_map_initialized)
> +		init_node_map (false);
> +	      node_map_initialized = true;
> +	      ncommon++;
> +	      unsigned speculative_id = 0;
> +	      struct indirect_target_info *item;
> +	      FOR_EACH_VEC_SAFE_ELT (e->indirect_info->indirect_call_targets, i,
> +				     item)
> +		{
> +		  if ((unsigned) i > e->indirect_info->num_of_ics)
> +		    break;
> +		  n2 = find_func_by_profile_id (item->common_target_id);
> +		  if (n2)
>  		    {
> -		      nmismatch++;
>  		      if (dump_file)
> -			fprintf (dump_file,
> -				 "Not speculating: "
> -				 "parameter count mistmatch\n");
> +			{
> +			  fprintf (dump_file,
> +				   "Indirect call -> direct call from"
> +				   " other module %s => %s, prob %3.2f\n",
> +				   n->dump_name (), n2->dump_name (),
> +				   item->common_target_probability
> +				     / (float) REG_BR_PROB_BASE);
> +			}
> +		      if (item->common_target_probability
> +			  < REG_BR_PROB_BASE / 2)
> +			{
> +			  nuseless++;
> +			  if (dump_file)
> +			    fprintf (
> +			      dump_file,
> +			      "Not speculating: probability is too low.\n");
> +			}
> +		      else if (!e->maybe_hot_p ())
> +			{
> +			  nuseless++;
> +			  if (dump_file)
> +			    fprintf (dump_file,
> +				     "Not speculating: call is cold.\n");
> +			}
> +		      else if (n2->get_availability () <= AVAIL_INTERPOSABLE
> +			       && n2->can_be_discarded_p ())
> +			{
> +			  nuseless++;
> +			  if (dump_file)
> +			    fprintf (dump_file,
> +				     "Not speculating: target is overwritable "
> +				     "and can be discarded.\n");
> +			}
> +		      else if (ipa_node_params_sum && ipa_edge_args_sum
> +			       && (!vec_safe_is_empty (
> +				 IPA_NODE_REF (n2)->descriptors))
> +			       && ipa_get_param_count (IPA_NODE_REF (n2))
> +				    != ipa_get_cs_argument_count (
> +				      IPA_EDGE_REF (e))
> +			       && (ipa_get_param_count (IPA_NODE_REF (n2))
> +				     >= ipa_get_cs_argument_count (
> +				       IPA_EDGE_REF (e))
> +				   || !stdarg_p (TREE_TYPE (n2->decl))))
> +			{
> +			  nmismatch++;
> +			  if (dump_file)
> +			    fprintf (dump_file, "Not speculating: "
> +						"parameter count mismatch\n");
> +			}
> +		      else if (e->indirect_info->polymorphic
> +			       && !opt_for_fn (n->decl, flag_devirtualize)
> +			       && !possible_polymorphic_call_target_p (e, n2))
> +			{
> +			  nimpossible++;
> +			  if (dump_file)
> +			    fprintf (dump_file,
> +				     "Not speculating: "
> +				     "function is not in the polymorphic "
> +				     "call target list\n");
> +			}
> +		      else
> +			{
> +			  /* Target may be overwritable, but profile says that
> +			     control flow goes to this particular implementation
> +			     of N2.  Speculate on the local alias to allow
> +			     inlining.
> +			     */
> +			  if (!n2->can_be_discarded_p ())
> +			    {
> +			      cgraph_node *alias;
> +			      alias = dyn_cast<cgraph_node *> (
> +				n2->noninterposable_alias ());
> +			      if (alias)
> +				n2 = alias;
> +			    }
> +			  nconverted++;
> +			  e->make_speculative (
> +			    n2,
> +			    e->count.apply_probability (
> +			      item->common_target_probability),
> +			    speculative_id);
> +			  update = true;
> +			  speculative_id++;
> +			}
>  		    }
> -		  else if (e->indirect_info->polymorphic
> -			   && !opt_for_fn (n->decl, flag_devirtualize)
> -			   && !possible_polymorphic_call_target_p (e, n2))
> +		  else
>  		    {
> -		      nimpossible++;
>  		      if (dump_file)
>  			fprintf (dump_file,
> -				 "Not speculating: "
> -				 "function is not in the polymorphic "
> -				 "call target list\n");
> +				 "Function with profile-id %i not found.\n",
> +				 item->common_target_id);
> +		      nunknown++;
>  		    }
> -		  else
> -		    {
> -		      /* Target may be overwritable, but profile says that
> -			 control flow goes to this particular implementation
> -			 of N2.  Speculate on the local alias to allow inlining.
> -		       */
> -		      if (!n2->can_be_discarded_p ())
> -			{
> -			  cgraph_node *alias;
> -			  alias = dyn_cast<cgraph_node *> (n2->noninterposable_alias ());
> -			  if (alias)
> -			    n2 = alias;
> -			}
> -		      nconverted++;
> -		      e->make_speculative
> -			(n2,
> -			 e->count.apply_probability
> -				     (e->indirect_info->common_target_probability));
> -		      update = true;
> -		    }
> -		}
> -	      else
> -		{
> -		  if (dump_file)
> -		    fprintf (dump_file, "Function with profile-id %i not found.\n",
> -			     e->indirect_info->common_target_id);
> -		  nunknown++;
>  		}
> +	      if (speculative_id < e->indirect_info->num_of_ics)
> +		e->indirect_info->num_of_ics = speculative_id;
>  	    }
> -	 }
> +	}
>         if (update)
>  	 ipa_update_overall_fn_summary (n);
>       }
> diff --git a/gcc/ipa-ref.h b/gcc/ipa-ref.h
> index 0d8e509c932..3e6562ec9d1 100644
> --- a/gcc/ipa-ref.h
> +++ b/gcc/ipa-ref.h
> @@ -59,6 +59,7 @@ public:
>    symtab_node *referred;
>    gimple *stmt;
>    unsigned int lto_stmt_uid;
> +  unsigned int speculative_id;
>    unsigned int referred_index;
>    ENUM_BITFIELD (ipa_ref_use) use:3;
>    unsigned int speculative:1;
> diff --git a/gcc/ipa.c b/gcc/ipa.c
> index 2496694124c..c1fe081a72d 100644
> --- a/gcc/ipa.c
> +++ b/gcc/ipa.c
> @@ -166,7 +166,7 @@ process_references (symtab_node *snode,
>     devirtualization happens.  After inlining still keep their declarations
>     around, so we can devirtualize to a direct call.
>  
> -   Also try to make trivial devirutalization when no or only one target is
> +   Also try to make trivial devirtualization when no or only one target is
>     possible.  */
>  
>  static void
> diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
> index bc0f0107333..1dc856d3c64 100644
> --- a/gcc/lto-cgraph.c
> +++ b/gcc/lto-cgraph.c
> @@ -238,6 +238,7 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
>    unsigned int uid;
>    intptr_t ref;
>    struct bitpack_d bp;
> +  unsigned len;
>  
>    if (edge->indirect_unknown_callee)
>      streamer_write_enum (ob->main_stream, LTO_symtab_tags, LTO_symtab_last_tag,
> @@ -265,6 +266,7 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
>    bp_pack_enum (&bp, cgraph_inline_failed_t,
>  	        CIF_N_REASONS, edge->inline_failed);
>    bp_pack_var_len_unsigned (&bp, uid);
> +  bp_pack_var_len_unsigned (&bp, edge->speculative_id);
>    bp_pack_value (&bp, edge->indirect_inlining_edge, 1);
>    bp_pack_value (&bp, edge->speculative, 1);
>    bp_pack_value (&bp, edge->call_stmt_cannot_inline_p, 1);
> @@ -291,11 +293,28 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
>    streamer_write_bitpack (&bp);
>    if (edge->indirect_unknown_callee)
>      {
> +      struct indirect_target_info *item;
> +      unsigned int i;
> +      len = edge->indirect_info->num_of_ics;
> +      gcc_assert (len <= GCOV_TOPN_VALUES);
> +
>        streamer_write_hwi_stream (ob->main_stream,
> -			         edge->indirect_info->common_target_id);
> -      if (edge->indirect_info->common_target_id)
> -	streamer_write_hwi_stream
> -	   (ob->main_stream, edge->indirect_info->common_target_probability);
> +				 edge->indirect_info->num_of_ics);
> +
> +      if (len)
> +	{
> +	  FOR_EACH_VEC_SAFE_ELT (edge->indirect_info->indirect_call_targets, i,
> +				 item)
> +	    {
> +	      if (i == edge->indirect_info->num_of_ics)
> +		break;

This is one of the places which will simplify with the vec::length.

> +	      streamer_write_hwi_stream (ob->main_stream,
> +					 item->common_target_id);
> +	      if (item->common_target_id)
> +		streamer_write_hwi_stream (ob->main_stream,
> +					   item->common_target_probability);
> +	    }
> +	}
>      }
>  }
>  
> @@ -688,6 +707,7 @@ lto_output_ref (struct lto_simple_output_block *ob, struct ipa_ref *ref,
>        if (ref->stmt)
>  	uid = gimple_uid (ref->stmt) + 1;
>        streamer_write_hwi_stream (ob->main_stream, uid);
> +      streamer_write_hwi_stream (ob->main_stream, ref->speculative_id);
>      }
>  }
>  
> @@ -1420,6 +1440,8 @@ input_ref (class lto_input_block *ib,
>    ref->speculative = speculative;
>    if (is_a <cgraph_node *> (referring_node))
>      ref->lto_stmt_uid = streamer_read_hwi (ib);
> +  if (is_a <cgraph_node *> (referring_node))
> +    ref->speculative_id = streamer_read_hwi (ib);

Here you want to write the condition just once.

>  }
>  
>  /* Read an edge from IB.  NODES points to a vector of previously read nodes for
> @@ -1433,11 +1455,12 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
>  {
>    struct cgraph_node *caller, *callee;
>    struct cgraph_edge *edge;
> -  unsigned int stmt_id;
> +  unsigned int stmt_id, speculative_id;
>    profile_count count;
>    cgraph_inline_failed_t inline_failed;
>    struct bitpack_d bp;
>    int ecf_flags = 0;
> +  unsigned i, len;
>  
>    caller = dyn_cast<cgraph_node *> (nodes[streamer_read_hwi (ib)]);
>    if (caller == NULL || caller->decl == NULL_TREE)
> @@ -1457,6 +1480,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
>    bp = streamer_read_bitpack (ib);
>    inline_failed = bp_unpack_enum (&bp, cgraph_inline_failed_t, CIF_N_REASONS);
>    stmt_id = bp_unpack_var_len_unsigned (&bp);
> +  speculative_id = bp_unpack_var_len_unsigned (&bp);
>  
>    if (indirect)
>      edge = caller->create_indirect_edge (NULL, 0, count);
> @@ -1466,6 +1490,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
>    edge->indirect_inlining_edge = bp_unpack_value (&bp, 1);
>    edge->speculative = bp_unpack_value (&bp, 1);
>    edge->lto_stmt_uid = stmt_id;
> +  edge->speculative_id = speculative_id;
>    edge->inline_failed = inline_failed;
>    edge->call_stmt_cannot_inline_p = bp_unpack_value (&bp, 1);
>    edge->can_throw_external = bp_unpack_value (&bp, 1);
> @@ -1485,9 +1510,22 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
>        if (bp_unpack_value (&bp, 1))
>  	ecf_flags |= ECF_RETURNS_TWICE;
>        edge->indirect_info->ecf_flags = ecf_flags;
> -      edge->indirect_info->common_target_id = streamer_read_hwi (ib);
> -      if (edge->indirect_info->common_target_id)
> -        edge->indirect_info->common_target_probability = streamer_read_hwi (ib);
> +
> +      len = streamer_read_hwi (ib);
> +      edge->indirect_info->num_of_ics = len;
> +
> +      gcc_assert (len <= GCOV_TOPN_VALUES);
> +
> +      if (len)
> +	{
> +	  for (i = 0; i < len; i++)
> +	    {
> +	      indirect_target_info item;

I would also suggest a constructor for indirect_target_info. You initialize it
multiple times.

> +	      item.common_target_id = streamer_read_hwi (ib);
> +	      item.common_target_probability = streamer_read_hwi (ib);
> +	      vec_safe_push (edge->indirect_info->indirect_call_targets, item);
> +	    }
> +	}
>      }
>  }
>  
> diff --git a/gcc/predict.c b/gcc/predict.c
> index 07f66aab7a3..67a859ea436 100644
> --- a/gcc/predict.c
> +++ b/gcc/predict.c
> @@ -763,7 +763,6 @@ dump_prediction (FILE *file, enum br_predictor predictor, int probability,
>        && bb->count.precise_p ()
>        && reason == REASON_NONE)
>      {
> -      gcc_assert (e->count ().precise_p ());
>        fprintf (file, ";;heuristics;%s;%" PRId64 ";%" PRId64 ";%.1f;\n",
>  	       predictor_info[predictor].name,
>  	       bb->count.to_gcov_type (), e->count ().to_gcov_type (),
> diff --git a/gcc/symtab.c b/gcc/symtab.c
> index b1589ea8a40..90a1f62d6a9 100644
> --- a/gcc/symtab.c
> +++ b/gcc/symtab.c
> @@ -603,6 +603,7 @@ symtab_node::create_reference (symtab_node *referred_node,
>    ref->referred = referred_node;
>    ref->stmt = stmt;
>    ref->lto_stmt_uid = 0;
> +  ref->speculative_id = 0;
>    ref->use = use_type;
>    ref->speculative = 0;
>  
> @@ -660,6 +661,7 @@ symtab_node::clone_references (symtab_node *node)
>        ref2 = create_reference (ref->referred, ref->use, ref->stmt);
>        ref2->speculative = speculative;
>        ref2->lto_stmt_uid = stmt_uid;
> +      ref2->speculative_id = ref->speculative_id;
>      }
>  }
>  
> @@ -678,6 +680,7 @@ symtab_node::clone_referring (symtab_node *node)
>        ref2 = ref->referring->create_reference (this, ref->use, ref->stmt);
>        ref2->speculative = speculative;
>        ref2->lto_stmt_uid = stmt_uid;
> +      ref2->speculative_id = ref->speculative_id;
>      }
>  }
>  
> @@ -693,6 +696,7 @@ symtab_node::clone_reference (ipa_ref *ref, gimple *stmt)
>    ref2 = create_reference (ref->referred, ref->use, stmt);
>    ref2->speculative = speculative;
>    ref2->lto_stmt_uid = stmt_uid;
> +  ref2->speculative_id = ref->speculative_id;
>    return ref2;
>  }
>  
> @@ -747,6 +751,7 @@ symtab_node::clear_stmts_in_references (void)
>        {
>  	r->stmt = NULL;
>  	r->lto_stmt_uid = 0;
> +	r->speculative_id = 0;
>        }
>  }
>  
> diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
> new file mode 100644
> index 00000000000..e0a83c2e067
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
> @@ -0,0 +1,35 @@
> +/* { dg-require-effective-target lto } */
> +/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
> +/* { dg-require-profiling "-fprofile-generate" } */
> +/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate --param indir-call-topn-profile=1" } */
> +
> +#include <stdio.h>
> +
> +typedef int (*fptr) (int);
> +int
> +one (int a);
> +
> +int
> +two (int a);
> +
> +fptr table[] = {&one, &two};
> +
> +int
> +main()
> +{
> +  int i, x;
> +  fptr p = &one;
> +
> +  x = one (3);
> +
> +  for (i = 0; i < 350000000; i++)
> +    {
> +      x = (*p) (3);
> +      p = table[x];
> +    }
> +  printf ("done:%d\n", x);
> +}
> +
> +/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* one transformation on insn" "profile_estimate" } } */
> +/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* two transformation on insn" "profile_estimate" } } */
> +
> diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
> new file mode 100644
> index 00000000000..a8c6e365fb9
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
> @@ -0,0 +1,22 @@
> +/* It seems there is no way to avoid the other source of mulitple
> +   source testcase from being compiled independently.  Just avoid
> +   error.  */
> +#ifdef DOJOB
> +int
> +one (int a)
> +{
> +  return 1;
> +}
> +
> +int
> +two (int a)
> +{
> +  return 0;
> +}
> +#else
> +int
> +main()
> +{
> +  return 0;
> +}
> +#endif
> diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
> new file mode 100644
> index 00000000000..aa3887fde83
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
> @@ -0,0 +1,42 @@
> +/* { dg-require-effective-target lto } */
> +/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
> +/* { dg-require-profiling "-fprofile-generate" } */
> +/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate --param indir-call-topn-profile=1" } */
> +
> +#include <stdio.h>
> +
> +typedef int (*fptr) (int);
> +int
> +one (int a);
> +
> +int
> +two (int a);
> +
> +fptr table[] = {&one, &two};
> +
> +int foo ()
> +{
> +  int i, x;
> +  fptr p = &one;
> +
> +  x = one (3);
> +
> +  for (i = 0; i < 350000000; i++)
> +    {
> +      x = (*p) (3);
> +      p = table[x];
> +    }
> +  return x;
> +}
> +
> +int
> +main()
> +{
> +  int x = foo ();
> +  printf ("done:%d\n", x);
> +}
> +
> +/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* one transformation on insn" "profile_estimate" } } */
> +/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* two transformation on insn" "profile_estimate" } } */
> +
> +
> diff --git a/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
> new file mode 100644
> index 00000000000..951bc7ddd19
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
> @@ -0,0 +1,38 @@
> +/* { dg-require-profiling "-fprofile-generate" } */
> +/* { dg-options "-O2 -fdump-ipa-profile --param indir-call-topn-profile=1" } */
> +
> +#include <stdio.h>
> +
> +typedef int (*fptr) (int);
> +int
> +one (int a)
> +{
> +  return 1;
> +}
> +
> +int
> +two (int a)
> +{
> +  return 0;
> +}
> +
> +fptr table[] = {&one, &two};
> +
> +int
> +main()
> +{
> +  int i, x;
> +  fptr p = &one;
> +
> +  one (3);
> +
> +  for (i = 0; i < 350000000; i++)
> +    {
> +      x = (*p) (3);
> +      p = table[x];
> +    }
> +  printf ("done:%d\n", x);
> +}
> +
> +/* { dg-final-use-not-autofdo { scan-ipa-dump "Indirect call -> direct call.* one transformation on insn" "profile" } } */
> +/* { dg-final-use-not-autofdo { scan-ipa-dump "Indirect call -> direct call.* two transformation on insn" "profile" } } */
> diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
> index 4311309acce..93d9269aa8f 100644
> --- a/gcc/tree-inline.c
> +++ b/gcc/tree-inline.c
> @@ -2167,6 +2167,26 @@ copy_bb (copy_body_data *id, basic_block bb,
>  
>  			  gcc_assert (!edge->indirect_unknown_callee);
>  			  old_edge->speculative_call_info (direct, indirect, ref);
> +			  while (old_edge->next_callee
> +				 && old_edge->next_callee->speculative
> +				 && indirect->indirect_info
> +				 && indirect->indirect_info->num_of_ics > 1)

Maybe indirect->indirect_info && indirect->indirect_info->num_of_ics can be
put into a function indirect->has_indirect_call_p () ?

> +			    {
> +			      /* Some speculative calls may contain more than
> +				 one direct target, loop iterate it to clone all
> +				 related direct edges before cloning the related
> +				 indirect edge.  */
> +			      id->dst_node->clone_reference (ref, stmt);
> +
> +			      edge = old_edge->next_callee;
> +			      edge = edge->clone (id->dst_node, call_stmt,
> +						  gimple_uid (stmt), num, den,
> +						  true);
> +			      old_edge = old_edge->next_callee;
> +			      gcc_assert (!edge->indirect_unknown_callee);
> +			      old_edge->speculative_call_info (direct, indirect,
> +							       ref);
> +			    }
>  
>  			  profile_count indir_cnt = indirect->count;
>  			  indirect = indirect->clone (id->dst_node, call_stmt,
> diff --git a/gcc/tree-profile.c b/gcc/tree-profile.c
> index 554a8c98419..f35bd3f9d7a 100644
> --- a/gcc/tree-profile.c
> +++ b/gcc/tree-profile.c
> @@ -74,8 +74,8 @@ static GTY(()) tree ic_tuple_callee_field;
>  /* Do initialization work for the edge profiler.  */
>  
>  /* Add code:
> -   __thread gcov*	__gcov_indirect_call_counters; // pointer to actual counter
> -   __thread void*	__gcov_indirect_call_callee; // actual callee address
> +   __thread gcov*	__gcov_indirect_call.counters; // pointer to actual counter
> +   __thread void*	__gcov_indirect_call.callee; // actual callee address
>     __thread int __gcov_function_counter; // time profiler function counter
>  */
>  static void
> @@ -384,7 +384,7 @@ gimple_gen_ic_profiler (histogram_value value, unsigned tag, unsigned base)
>        f_1 = foo;
>        __gcov_indirect_call.counters = &__gcov4.main[0];
>        PROF_9 = f_1;
> -      __gcov_indirect_call_callee = PROF_9;
> +      __gcov_indirect_call.callee = PROF_9;
>        _4 = f_1 ();
>     */
>  
> @@ -447,11 +447,11 @@ gimple_gen_ic_func_profiler (void)
>  
>    /* Insert code:
>  
> -     if (__gcov_indirect_call_callee != NULL)
> +     if (__gcov_indirect_call.callee != NULL)
>         __gcov_indirect_call_profiler_v3 (profile_id, &current_function_decl);
>  
>       The function __gcov_indirect_call_profiler_v3 is responsible for
> -     resetting __gcov_indirect_call_callee to NULL.  */
> +     resetting __gcov_indirect_call.callee to NULL.  */
>  
>    gimple_stmt_iterator gsi = gsi_start_bb (cond_bb);
>    void0 = build_int_cst (ptr_type_node, 0);
> @@ -893,7 +893,7 @@ pass_ipa_tree_profile::gate (function *)
>  {
>    /* When profile instrumentation, use or test coverage shall be performed.
>       But for AutoFDO, this there is no instrumentation, thus this pass is
> -     diabled.  */
> +     disabled.  */
>    return (!in_lto_p && !flag_auto_profile
>  	  && (flag_branch_probabilities || flag_test_coverage
>  	      || profile_arc_flag));
> diff --git a/gcc/value-prof.c b/gcc/value-prof.c
> index 759458868a8..cbcc104bd5c 100644
> --- a/gcc/value-prof.c
> +++ b/gcc/value-prof.c

Please rebase changes in this file. I installed recently changes that collide with your patch.

Thanks,
Martin

> @@ -1404,11 +1404,10 @@ gimple_ic (gcall *icall_stmt, struct cgraph_node *direct_call,
>    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:
> - */
> +/* There maybe multiple indirect targets in histogram.  Check every
> +   indirect/virtual call if callee function exists, if not exist, leave it to
> +   LTO stage for later process.  Modify code of this indirect call to an if-else
> +   structure in ipa-profile finally.  */
>  
>  static bool
>  gimple_ic_transform (gimple_stmt_iterator *gsi)
> @@ -1432,54 +1431,65 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
>    if (!histogram)
>      return false;
>  
> -  if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
> -				  &count, &all))
> -    return false;
> +  count = 0;
> +  all = histogram->hvalue.counters[0];
>  
> -  if (4 * count <= 3 * all)
> -    return false;
> +  for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
> +    {
> +      if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
> +				      &count, &all, j))
> +	continue;
>  
> -  direct_call = find_func_by_profile_id ((int)val);
> +      if (4 * count <= all)
> +	continue;
>  
> -  if (direct_call == NULL)
> -    {
> -      if (val)
> +      direct_call = find_func_by_profile_id ((int) val);
> +
> +      if (direct_call == NULL)
> +	{
> +	  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);
> +		}
> +	    }
> +	  continue;
> +	}
> +
> +      if (!check_ic_target (stmt, direct_call))
>  	{
>  	  if (dump_file)
>  	    {
> -	      fprintf (dump_file, "Indirect call -> direct call from other module");
> +	      fprintf (dump_file, "Indirect call -> direct call ");
>  	      print_generic_expr (dump_file, gimple_call_fn (stmt), TDF_SLIM);
> -	      fprintf (dump_file, "=> %i (will resolve only with LTO)\n", (int)val);
> +	      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;
>  	}
> -      return false;
> -    }
>  
> -  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");
> +	  fprintf (dump_file,
> +		   " transformation on insn postponed to ipa-profile");
>  	  print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
> +	  fprintf (dump_file, "hist->count %" PRId64 " hist->all %" PRId64 "\n",
> +		   count, all);
>  	}
> -      gimple_remove_histogram_value (cfun, stmt, histogram);
> -      return false;
> -    }
> -
> -  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;
> 

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v3] Missed function specialization + partial devirtualization
  2019-08-13 13:16     ` Martin Liška
@ 2019-08-13 13:21       ` Martin Liška
  2019-09-25  3:46       ` [PATCH v4] " luoxhu
  1 sibling, 0 replies; 38+ messages in thread
From: Martin Liška @ 2019-08-13 13:21 UTC (permalink / raw)
  To: luoxhu, gcc-patches; +Cc: hubicka, segher, wschmidt

And I would also suggest to come up with parameter that will
drive minimum probability, maximum number of promotions and
maybe minimal number of edge executions to consider a speculation.

Clang provides all these:
https://github.com/microsoft/llvm-1/blob/master/lib/Analysis/IndirectCallPromotionAnalysis.cpp#L37

Martin

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH v4] Missed function specialization + partial devirtualization
  2019-08-13 13:16     ` Martin Liška
  2019-08-13 13:21       ` Martin Liška
@ 2019-09-25  3:46       ` luoxhu
  2019-09-25 10:57         ` Martin Liška
  1 sibling, 1 reply; 38+ messages in thread
From: luoxhu @ 2019-09-25  3:46 UTC (permalink / raw)
  To: Martin Liška, gcc-patches; +Cc: hubicka, segher, wschmidt

Hi,

Sorry for replying so late due to cauldron conference and other LTO issues
I was working on.

v4 Changes:
 1. Rebase to trunk.
 2. Remove num_of_ics and use vector's length to avoid redundancy.
 3. Update the code in ipa-profile.c to improve review feasibility.
 4. Add function has_indirect_call_p and has_multiple_indirect_call_p.
 5. For parameter control, I will leave it to next patch as it is a
    relative independent function.  Currently, maximum number of
    promotions is GCOV_TOPN_VALUES as only 4 profiling value limited
    from profile-generate, therefore minimum probability is adjusted to
    25% in value-prof.c, it was 75% also by hard code for single
    indirect target.  No control to minimal number of edge
    executions yet.  What's more, this patch is a bit large now.

This patch aims to fix PR69678 caused by PGO indirect call profiling
performance issues.
The bug that profiling data is never working was fixed by Martin's pull
back of topN patches, performance got GEOMEAN ~1% improvement(+24% for
511.povray_r specifically).
Still, currently the default profile only generates SINGLE indirect target
that called more than 75%.  This patch leverages MULTIPLE indirect
targets use in LTO-WPA and LTO-LTRANS stage, as a result, function
specialization, profiling, partial devirtualization, inlining and
cloning could be done successfully based on it.
Performance can get improved from 0.70 sec to 0.38 sec on simple tests.
Details are:
  1.  PGO with topn is enabled by default now, but only one indirect
  target edge will be generated in ipa-profile pass, so add variables to enable
  multiple speculative edges through passes, speculative_id will record the
  direct edge index bind to the indirect edge, indirect_call_targets length
  records how many direct edges owned by the indirect edge, postpone gimple_ic
  to ipa-profile like default as inline pass will decide whether it is benefit
  to transform indirect call.
  2.  Use speculative_id to track and search the reference node matched
  with the direct edge's callee for multiple targets.  Actually, it is the
  caller's responsibility to handle the direct edges mapped to same indirect
  edge.  speculative_call_info will return one of the direct edge specified,
  this will leverage current IPA edge process framework mostly.
  3.  Enable LTO WPA/LTRANS stage multiple indirect call targets analysis for
  profile full support in ipa passes and cgraph_edge functions.  speculative_id
  can be set by make_speculative id when multiple targets are binded to
  one indirect edge, and cloned if new edge is cloned.  speculative_id
  is streamed out and stream int by lto like lto_stmt_uid.
  4.  Add 1 in module testcase and 2 cross module testcases.
  5.  Bootstrap and regression test passed on Power8-LE.  No function
  and performance regression for SPEC2017.

gcc/ChangeLog

	2019-09-25  Xiong Hu Luo  <luoxhu@linux.ibm.com>

	PR ipa/69678
	* cgraph.c (symbol_table::create_edge): Init speculative_id.
	(cgraph_edge::make_speculative): Add param for setting speculative_id.
	(cgraph_edge::speculative_call_info): Find reference by
	speculative_id for multiple indirect targets.
	(cgraph_edge::resolve_speculation): Decrease the speculations
	for indirect edge, drop it's speculative if not direct target
	left.
	(cgraph_edge::redirect_call_stmt_to_callee): Likewise.
	(cgraph_node::verify_node): Don't report error if speculative
	edge not include statement.
	(cgraph_edge::has_multiple_indirect_call_p): New function.
	(cgraph_edge::has_indirect_call_p): New function.
	* cgraph.h (struct indirect_target_info): New struct.
	(indirect_call_targets): New vector variable.
	(make_speculative): Add param for setting speculative_id.
	(cgraph_edge::has_multiple_indirect_call_p): New declare.
	(cgraph_edge::has_indirect_call_p): New declare.
	(speculative_id): New variable.
	* cgraphclones.c (cgraph_node::create_clone): Clone speculative_id.
	* ipa-inline.c (inline_small_functions): Fix iterator update.
	* ipa-profile.c (ipa_profile_generate_summary): Add indirect
	multiple targets logic.
	(ipa_profile): Likewise.
	* ipa-ref.h (speculative_id): New variable.
	* ipa.c (process_references): Fix typo.
	* lto-cgraph.c (lto_output_edge): Add indirect multiple targets
	logic.  Stream out speculative_id.
	(input_edge): Likewise.
	* predict.c (dump_prediction): Remove edges count assert to be
	precise.
	* symtab.c (symtab_node::create_reference): Init speculative_id.
	(symtab_node::clone_references): Clone speculative_id.
	(symtab_node::clone_referring): Clone speculative_id.
	(symtab_node::clone_reference): Clone speculative_id.
	(symtab_node::clear_stmts_in_references): Clear speculative_id.
	* tree-inline.c (copy_bb): Duplicate all the speculative edges
	if indirect call contains multiple speculative targets.
	* tree-profile.c (gimple_gen_ic_profiler): Use the new variable
	__gcov_indirect_call.counters and __gcov_indirect_call.callee.
	(gimple_gen_ic_func_profiler): Likewise.
	(pass_ipa_tree_profile::gate): Fix comment typos.
	* value-prof.c  (gimple_ic_transform): Handle topn case.
	Fix comment typos.

gcc/testsuite/ChangeLog

	2019-09-25  Xiong Hu Luo  <luoxhu@linux.ibm.com>

	PR ipa/69678
	* gcc.dg/tree-prof/indir-call-prof-topn.c: New testcase.
	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c: New testcase.
	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c: New testcase.
	* gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c: New testcase.
---
 gcc/cgraph.c                                  | 90 ++++++++++++++++-
 gcc/cgraph.h                                  | 29 +++++-
 gcc/cgraphclones.c                            |  1 +
 gcc/ipa-inline.c                              | 15 +--
 gcc/ipa-profile.c                             | 96 ++++++++++++++-----
 gcc/ipa-ref.h                                 |  1 +
 gcc/ipa.c                                     |  2 +-
 gcc/lto-cgraph.c                              | 57 +++++++++--
 gcc/predict.c                                 |  1 -
 gcc/symtab.c                                  |  5 +
 .../tree-prof/crossmodule-indir-call-topn-1.c | 35 +++++++
 .../crossmodule-indir-call-topn-1a.c          | 22 +++++
 .../tree-prof/crossmodule-indir-call-topn-2.c | 42 ++++++++
 .../gcc.dg/tree-prof/indir-call-prof-topn.c   | 38 ++++++++
 gcc/tree-inline.c                             | 19 ++++
 gcc/tree-profile.c                            | 12 +--
 gcc/value-prof.c                              | 86 +++++++++--------
 17 files changed, 452 insertions(+), 99 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c

diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index 843891e9e56..9a28aca435f 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -860,6 +860,7 @@ symbol_table::create_edge (cgraph_node *caller, cgraph_node *callee,
   edge->prev_callee = NULL;
   edge->next_callee = NULL;
   edge->lto_stmt_uid = 0;
+  edge->speculative_id = 0;
 
   edge->count = count;
 
@@ -1051,7 +1052,8 @@ cgraph_edge::remove (void)
    Return direct edge created.  */
 
 cgraph_edge *
-cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
+cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count,
+			       unsigned int speculative_id)
 {
   cgraph_node *n = caller;
   ipa_ref *ref = NULL;
@@ -1069,11 +1071,13 @@ cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
   else
     e2->can_throw_external = can_throw_external;
   e2->lto_stmt_uid = lto_stmt_uid;
+  e2->speculative_id = speculative_id;
   e2->in_polymorphic_cdtor = in_polymorphic_cdtor;
   count -= e2->count;
   symtab->call_edge_duplication_hooks (this, e2);
   ref = n->create_reference (n2, IPA_REF_ADDR, call_stmt);
   ref->lto_stmt_uid = lto_stmt_uid;
+  ref->speculative_id = speculative_id;
   ref->speculative = speculative;
   n2->mark_address_taken ();
   return e2;
@@ -1087,6 +1091,38 @@ cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
    call) and if one of them exists, all of them must exist.
 
    Given speculative call edge, return all three components.
+
+   For some indirect edge, it may maps to multiple direct edges, i.e.  1:N.
+   check the speculative_id to return all the three components for specified
+   direct edge or indirect edge.
+   If input is indirect, caller of this function will get the direct edge one by
+   one, get_edge will just return one of the direct edge mapped to the indirect
+   edge, the returned direct edge will be resolved or redirected by the caller,
+   then number of indirect calls (speculations) is deceased in each access.
+   If input is direct, this function will get the indirect edge and reference
+   with matched speculative_id, the returned edge will also be resolved or
+   redirected, decrease the speculations accordingly.
+   Speculations of indirect edge will be dropped only if all direct edges
+   be handled.
+
+   e.g.  for indirect edge E statement "call call_dest":
+
+   Redirect N3 after redirected N2:
+
+   if (call_dest == N2)
+     n2 ();
+   else if (call_dest == N3)
+     n3 ();
+   else
+     call call_dest
+
+   Resolve N3 and only redirect N2:
+
+   if (call_dest == N2)
+     n2 ();
+   else
+     call call_dest
+
  */
 
 void
@@ -1126,7 +1162,7 @@ cgraph_edge::speculative_call_info (cgraph_edge *&direct,
 
   reference = NULL;
   for (i = 0; e->caller->iterate_reference (i, ref); i++)
-    if (ref->speculative
+    if (ref->speculative && ref->speculative_id == e->speculative_id
 	&& ((ref->stmt && ref->stmt == e->call_stmt)
 	    || (!ref->stmt && ref->lto_stmt_uid == e->lto_stmt_uid)))
       {
@@ -1187,7 +1223,21 @@ cgraph_edge::resolve_speculation (tree callee_decl)
          in the functions inlined through it.  */
     }
   edge->count += e2->count;
-  edge->speculative = false;
+  /* edge is indirect, e2 is direct.  If edge contains multiple speculations,
+     remove one of speculations for this indirect edge, then if edge still
+     contains direct target, keep the speculation, next direct target
+     will continue use it.  Give up speculation completely if no direct
+     target is left for this indirect edge.  */
+  if (edge->has_indirect_call_p ())
+    {
+      /* As the direct targets are sorted by decrease, delete the first target
+	 when it is resolved.  */
+      edge->indirect_info->indirect_call_targets->ordered_remove (0);
+      if (!edge->indirect_info->indirect_call_targets->length ())
+	edge->speculative = false;
+    }
+  else
+    edge->speculative = false;
   e2->speculative = false;
   ref->remove_reference ();
   if (e2->indirect_unknown_callee || e2->inline_failed)
@@ -1321,7 +1371,21 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
 	  e->caller->set_call_stmt_including_clones (e->call_stmt, new_stmt,
 						     false);
 	  e->count = gimple_bb (e->call_stmt)->count;
-	  e2->speculative = false;
+	  /* edge is direct, e2 is indirect here.  If e2 contains multiple
+	     speculations, remove one of speculations for this indirect edge,
+	     then if e2 still contains direct target, keep the speculation,
+	     next direct target will continue use it.  Give up speculation
+	     completely if no direct target is left for this indirect e2.  */
+	  if (e2->has_indirect_call_p ())
+	    {
+	      /* As the direct targets are sorted by decrease, delete the first
+		 target when it is redirected.  */
+	      e2->indirect_info->indirect_call_targets->ordered_remove (0);
+	      if (!e2->indirect_info->indirect_call_targets->length ())
+		e2->speculative = false;
+	    }
+	  else
+	    e2->speculative = false;
 	  e2->count = gimple_bb (e2->call_stmt)->count;
 	  ref->speculative = false;
 	  ref->stmt = NULL;
@@ -3445,7 +3509,7 @@ cgraph_node::verify_node (void)
 
       for (e = callees; e; e = e->next_callee)
 	{
-	  if (!e->aux)
+	  if (!e->aux && !e->speculative)
 	    {
 	      error ("edge %s->%s has no corresponding call_stmt",
 		     identifier_to_locale (e->caller->name ()),
@@ -3872,6 +3936,22 @@ cgraph_edge::possibly_call_in_translation_unit_p (void)
   return node->get_availability () >= AVAIL_AVAILABLE;
 }
 
+/* Return true if this edge has multiple indirect call targets.  */
+bool
+cgraph_edge::has_multiple_indirect_call_p (void)
+{
+  return indirect_info && indirect_info->indirect_call_targets
+	 && indirect_info->indirect_call_targets->length () > 1;
+}
+
+/* Return true if this edge has at least one indirect call target.  */
+bool
+cgraph_edge::has_indirect_call_p (void)
+{
+  return indirect_info && indirect_info->indirect_call_targets
+	 && indirect_info->indirect_call_targets->length ();
+}
+
 /* A stashed copy of "symtab" for use by selftest::symbol_table_test.
    This needs to be a global so that it can be a GC root, and thus
    prevent the stashed copy from being garbage-collected if the GC runs
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 4c54210123a..33c8454c4e0 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -1636,6 +1636,16 @@ private:
   void make_speculative (tree otr_type = NULL);
 };
 
+/* Structure containing indirect target information from profile.  */
+
+struct GTY (()) indirect_target_info
+{
+  /* Profile_id of common target obtained from profile.  */
+  unsigned int common_target_id;
+  /* Probability that call will land in function with COMMON_TARGET_ID.  */
+  int common_target_probability;
+};
+
 /* Structure containing additional information about an indirect call.  */
 
 class GTY(()) cgraph_indirect_call_info
@@ -1654,10 +1664,9 @@ public:
   int param_index;
   /* ECF flags determined from the caller.  */
   int ecf_flags;
-  /* Profile_id of common target obtrained from profile.  */
-  int common_target_id;
-  /* Probability that call will land in function with COMMON_TARGET_ID.  */
-  int common_target_probability;
+
+  /* An indirect call may contain one or multiple call targets.  */
+  vec<indirect_target_info, va_gc> *indirect_call_targets;
 
   /* Set when the call is a virtual call with the parameter being the
      associated object pointer rather than a simple direct call.  */
@@ -1714,7 +1723,8 @@ public:
   /* Turn edge into speculative call calling N2. Update
      the profile so the direct call is taken COUNT times
      with FREQUENCY.  */
-  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count);
+  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count,
+				 unsigned int speculative_id = 0);
 
    /* Given speculative call edge, return all three components.  */
   void speculative_call_info (cgraph_edge *&direct, cgraph_edge *&indirect,
@@ -1773,6 +1783,12 @@ public:
      be internal to the current translation unit.  */
   bool possibly_call_in_translation_unit_p (void);
 
+  /* Return true if this edge has multiple indirect call targets.  */
+  bool has_multiple_indirect_call_p (void);
+
+  /* Return true if this edge has at least one indirect call target.  */
+  bool has_indirect_call_p (void);
+
   /* Expected number of executions: calculated in profile.c.  */
   profile_count count;
   cgraph_node *caller;
@@ -1792,6 +1808,9 @@ public:
   /* The stmt_uid of call_stmt.  This is used by LTO to recover the call_stmt
      when the function is serialized in.  */
   unsigned int lto_stmt_uid;
+  /* speculative id is used by multiple indirect targets when the function is
+   speculated.  */
+  unsigned int speculative_id;
   /* Whether this edge was made direct by indirect inlining.  */
   unsigned int indirect_inlining_edge : 1;
   /* Whether this edge describes an indirect call with an undetermined
diff --git a/gcc/cgraphclones.c b/gcc/cgraphclones.c
index fa753697c78..5dbd8d90b77 100644
--- a/gcc/cgraphclones.c
+++ b/gcc/cgraphclones.c
@@ -128,6 +128,7 @@ cgraph_edge::clone (cgraph_node *n, gcall *call_stmt, unsigned stmt_uid,
   new_edge->inline_failed = inline_failed;
   new_edge->indirect_inlining_edge = indirect_inlining_edge;
   new_edge->lto_stmt_uid = stmt_uid;
+  new_edge->speculative_id = speculative_id;
   /* Clone flags that depend on call_stmt availability manually.  */
   new_edge->can_throw_external = can_throw_external;
   new_edge->call_stmt_cannot_inline_p = call_stmt_cannot_inline_p;
diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
index b62d280eb25..6136214f9ac 100644
--- a/gcc/ipa-inline.c
+++ b/gcc/ipa-inline.c
@@ -1881,12 +1881,15 @@ inline_small_functions (void)
 	}
       if (has_speculative)
 	for (edge = node->callees; edge; edge = next)
-	  if (edge->speculative && !speculation_useful_p (edge,
-							  edge->aux != NULL))
-	    {
-	      edge->resolve_speculation ();
-	      update = true;
-	    }
+	  {
+	    next = edge->next_callee;
+	    if (edge->speculative
+		&& !speculation_useful_p (edge, edge->aux != NULL))
+	      {
+		edge->resolve_speculation ();
+		update = true;
+	      }
+	  }
       if (update)
 	{
 	  struct cgraph_node *where = node->global.inlined_to
diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
index 970dba39c80..342e8ea05d1 100644
--- a/gcc/ipa-profile.c
+++ b/gcc/ipa-profile.c
@@ -192,23 +192,35 @@ ipa_profile_generate_summary (void)
 		  if (h)
 		    {
 		      gcov_type val, count, all;
-		      if (get_nth_most_common_value (NULL, "indirect call", h,
-						     &val, &count, &all))
+		      struct cgraph_edge *e = node->get_edge (stmt);
+		      if (e && !e->indirect_unknown_callee)
+			continue;
+
+		      struct indirect_target_info item;
+		      for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
 			{
-			  struct cgraph_edge * e = node->get_edge (stmt);
-			  if (e && !e->indirect_unknown_callee)
+			  if (!get_nth_most_common_value (NULL, "indirect call",
+							  h, &val, &count, &all,
+							  j))
+			    continue;
+
+			  if (val == 0)
 			    continue;
 
-			  e->indirect_info->common_target_id = val;
-			  e->indirect_info->common_target_probability
+			  item.common_target_id = val;
+			  item.common_target_probability
 			    = GCOV_COMPUTE_SCALE (count, all);
-			  if (e->indirect_info->common_target_probability > REG_BR_PROB_BASE)
+			  if (item.common_target_probability > REG_BR_PROB_BASE)
 			    {
 			      if (dump_file)
-				fprintf (dump_file, "Probability capped to 1\n");
-			      e->indirect_info->common_target_probability = REG_BR_PROB_BASE;
+				fprintf (dump_file,
+					 "Probability capped to 1\n");
+			      item.common_target_probability = REG_BR_PROB_BASE;
 			    }
+			  vec_safe_push (
+			    e->indirect_info->indirect_call_targets, item);
 			}
+
 		      gimple_remove_histogram_value (DECL_STRUCT_FUNCTION (node->decl),
 						      stmt, h);
 		    }
@@ -492,6 +504,7 @@ ipa_profile (void)
   int nindirect = 0, ncommon = 0, nunknown = 0, nuseless = 0, nconverted = 0;
   int nmismatch = 0, nimpossible = 0;
   bool node_map_initialized = false;
+  gcov_type threshold;
 
   if (dump_file)
     dump_histogram (dump_file, histogram);
@@ -500,14 +513,12 @@ ipa_profile (void)
       overall_time += histogram[i]->count * histogram[i]->time;
       overall_size += histogram[i]->size;
     }
+  threshold = 0;
   if (overall_time)
     {
-      gcov_type threshold;
-
       gcc_assert (overall_size);
 
       cutoff = (overall_time * PARAM_VALUE (HOT_BB_COUNT_WS_PERMILLE) + 500) / 1000;
-      threshold = 0;
       for (i = 0; cumulated < cutoff; i++)
 	{
 	  cumulated += histogram[i]->count * histogram[i]->time;
@@ -543,7 +554,7 @@ ipa_profile (void)
   histogram.release ();
   histogram_pool.release ();
 
-  /* Produce speculative calls: we saved common traget from porfiling into
+  /* Produce speculative calls: we saved common target from profiling into
      e->common_target_id.  Now, at link time, we can look up corresponding
      function node and produce speculative call.  */
 
@@ -558,13 +569,37 @@ ipa_profile (void)
 	{
 	  if (n->count.initialized_p ())
 	    nindirect++;
-	  if (e->indirect_info->common_target_id)
+	  if (e->has_indirect_call_p ())
 	    {
 	      if (!node_map_initialized)
-	        init_node_map (false);
+		init_node_map (false);
 	      node_map_initialized = true;
 	      ncommon++;
-	      n2 = find_func_by_profile_id (e->indirect_info->common_target_id);
+
+	      if (in_lto_p)
+		{
+		  if (dump_file)
+		    {
+		      fprintf (dump_file,
+			       "Updating hotness threshold in LTO mode.\n");
+		      fprintf (dump_file, "Updated min count: %" PRId64 "\n",
+			       (int64_t) threshold);
+		    }
+		  set_hot_bb_threshold (threshold
+		    / e->indirect_info->indirect_call_targets->length ());
+		}
+
+	      unsigned speculative_id = 0;
+	      struct indirect_target_info *item;
+	      /* The code below is not formatted yet for review convenience.
+		 Move to a seprate small function is not easy as too many local
+		 variables used in it.  Need format and remove this comments
+		 once got approved.  */
+	      FOR_EACH_VEC_SAFE_ELT (e->indirect_info->indirect_call_targets, i,
+				     item)
+	       {
+	      bool speculative_found = false;
+	      n2 = find_func_by_profile_id (item->common_target_id);
 	      if (n2)
 		{
 		  if (dump_file)
@@ -573,11 +608,10 @@ ipa_profile (void)
 			       " other module %s => %s, prob %3.2f\n",
 			       n->dump_name (),
 			       n2->dump_name (),
-			       e->indirect_info->common_target_probability
-			       / (float)REG_BR_PROB_BASE);
+			       item->common_target_probability
+				 / (float) REG_BR_PROB_BASE);
 		    }
-		  if (e->indirect_info->common_target_probability
-		      < REG_BR_PROB_BASE / 2)
+		  if (item->common_target_probability < REG_BR_PROB_BASE / 2)
 		    {
 		      nuseless++;
 		      if (dump_file)
@@ -613,7 +647,7 @@ ipa_profile (void)
 		      if (dump_file)
 			fprintf (dump_file,
 				 "Not speculating: "
-				 "parameter count mistmatch\n");
+				 "parameter count mismatch\n");
 		    }
 		  else if (e->indirect_info->polymorphic
 			   && !opt_for_fn (n->decl, flag_devirtualize)
@@ -640,20 +674,30 @@ ipa_profile (void)
 			    n2 = alias;
 			}
 		      nconverted++;
-		      e->make_speculative
-			(n2,
-			 e->count.apply_probability
-				     (e->indirect_info->common_target_probability));
+		      e->make_speculative (n2,
+					   e->count.apply_probability (
+					     item->common_target_probability),
+					   speculative_id);
 		      update = true;
+		      speculative_id++;
+		      speculative_found = true;
 		    }
 		}
 	      else
 		{
 		  if (dump_file)
 		    fprintf (dump_file, "Function with profile-id %i not found.\n",
-			     e->indirect_info->common_target_id);
+			     item->common_target_id);
 		  nunknown++;
 		}
+	      if (!speculative_found)
+		{
+		  /* Remove item from indirect_call_targets if no
+		     speculative edge generated, rollback the iteration.  */
+		  e->indirect_info->indirect_call_targets->ordered_remove (i);
+		  i--;
+		}
+	       }
 	    }
 	 }
        if (update)
diff --git a/gcc/ipa-ref.h b/gcc/ipa-ref.h
index 0d8e509c932..3e6562ec9d1 100644
--- a/gcc/ipa-ref.h
+++ b/gcc/ipa-ref.h
@@ -59,6 +59,7 @@ public:
   symtab_node *referred;
   gimple *stmt;
   unsigned int lto_stmt_uid;
+  unsigned int speculative_id;
   unsigned int referred_index;
   ENUM_BITFIELD (ipa_ref_use) use:3;
   unsigned int speculative:1;
diff --git a/gcc/ipa.c b/gcc/ipa.c
index 6b84e1f9bda..a10b0603f14 100644
--- a/gcc/ipa.c
+++ b/gcc/ipa.c
@@ -166,7 +166,7 @@ process_references (symtab_node *snode,
    devirtualization happens.  After inlining still keep their declarations
    around, so we can devirtualize to a direct call.
 
-   Also try to make trivial devirutalization when no or only one target is
+   Also try to make trivial devirtualization when no or only one target is
    possible.  */
 
 static void
diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
index bc0f0107333..61380dcc7b8 100644
--- a/gcc/lto-cgraph.c
+++ b/gcc/lto-cgraph.c
@@ -238,6 +238,7 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
   unsigned int uid;
   intptr_t ref;
   struct bitpack_d bp;
+  unsigned len;
 
   if (edge->indirect_unknown_callee)
     streamer_write_enum (ob->main_stream, LTO_symtab_tags, LTO_symtab_last_tag,
@@ -265,6 +266,7 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
   bp_pack_enum (&bp, cgraph_inline_failed_t,
 	        CIF_N_REASONS, edge->inline_failed);
   bp_pack_var_len_unsigned (&bp, uid);
+  bp_pack_var_len_unsigned (&bp, edge->speculative_id);
   bp_pack_value (&bp, edge->indirect_inlining_edge, 1);
   bp_pack_value (&bp, edge->speculative, 1);
   bp_pack_value (&bp, edge->call_stmt_cannot_inline_p, 1);
@@ -291,11 +293,27 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
   streamer_write_bitpack (&bp);
   if (edge->indirect_unknown_callee)
     {
-      streamer_write_hwi_stream (ob->main_stream,
-			         edge->indirect_info->common_target_id);
-      if (edge->indirect_info->common_target_id)
-	streamer_write_hwi_stream
-	   (ob->main_stream, edge->indirect_info->common_target_probability);
+      struct indirect_target_info *item;
+      unsigned int i;
+      len = edge->has_indirect_call_p ()
+	      ? edge->indirect_info->indirect_call_targets->length ()
+	      : 0;
+      gcc_assert (len <= GCOV_TOPN_VALUES);
+
+      streamer_write_hwi_stream (ob->main_stream, len);
+
+      if (len)
+	{
+	  FOR_EACH_VEC_SAFE_ELT (edge->indirect_info->indirect_call_targets, i,
+				 item)
+	    {
+	      streamer_write_hwi_stream (ob->main_stream,
+					 item->common_target_id);
+	      if (item->common_target_id)
+		streamer_write_hwi_stream (ob->main_stream,
+					   item->common_target_probability);
+	    }
+	}
     }
 }
 
@@ -688,6 +706,7 @@ lto_output_ref (struct lto_simple_output_block *ob, struct ipa_ref *ref,
       if (ref->stmt)
 	uid = gimple_uid (ref->stmt) + 1;
       streamer_write_hwi_stream (ob->main_stream, uid);
+      streamer_write_hwi_stream (ob->main_stream, ref->speculative_id);
     }
 }
 
@@ -1419,7 +1438,10 @@ input_ref (class lto_input_block *ib,
   ref = referring_node->create_reference (node, use);
   ref->speculative = speculative;
   if (is_a <cgraph_node *> (referring_node))
-    ref->lto_stmt_uid = streamer_read_hwi (ib);
+    {
+      ref->lto_stmt_uid = streamer_read_hwi (ib);
+      ref->speculative_id = streamer_read_hwi (ib);
+    }
 }
 
 /* Read an edge from IB.  NODES points to a vector of previously read nodes for
@@ -1433,11 +1455,12 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
 {
   struct cgraph_node *caller, *callee;
   struct cgraph_edge *edge;
-  unsigned int stmt_id;
+  unsigned int stmt_id, speculative_id;
   profile_count count;
   cgraph_inline_failed_t inline_failed;
   struct bitpack_d bp;
   int ecf_flags = 0;
+  unsigned i, len;
 
   caller = dyn_cast<cgraph_node *> (nodes[streamer_read_hwi (ib)]);
   if (caller == NULL || caller->decl == NULL_TREE)
@@ -1457,6 +1480,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
   bp = streamer_read_bitpack (ib);
   inline_failed = bp_unpack_enum (&bp, cgraph_inline_failed_t, CIF_N_REASONS);
   stmt_id = bp_unpack_var_len_unsigned (&bp);
+  speculative_id = bp_unpack_var_len_unsigned (&bp);
 
   if (indirect)
     edge = caller->create_indirect_edge (NULL, 0, count);
@@ -1466,6 +1490,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
   edge->indirect_inlining_edge = bp_unpack_value (&bp, 1);
   edge->speculative = bp_unpack_value (&bp, 1);
   edge->lto_stmt_uid = stmt_id;
+  edge->speculative_id = speculative_id;
   edge->inline_failed = inline_failed;
   edge->call_stmt_cannot_inline_p = bp_unpack_value (&bp, 1);
   edge->can_throw_external = bp_unpack_value (&bp, 1);
@@ -1485,9 +1510,21 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
       if (bp_unpack_value (&bp, 1))
 	ecf_flags |= ECF_RETURNS_TWICE;
       edge->indirect_info->ecf_flags = ecf_flags;
-      edge->indirect_info->common_target_id = streamer_read_hwi (ib);
-      if (edge->indirect_info->common_target_id)
-        edge->indirect_info->common_target_probability = streamer_read_hwi (ib);
+
+      len = streamer_read_hwi (ib);
+
+      gcc_assert (len <= GCOV_TOPN_VALUES);
+
+      if (len)
+	{
+	  indirect_target_info item;
+	  for (i = 0; i < len; i++)
+	    {
+	      item.common_target_id = streamer_read_hwi (ib);
+	      item.common_target_probability = streamer_read_hwi (ib);
+	      vec_safe_push (edge->indirect_info->indirect_call_targets, item);
+	    }
+	}
     }
 }
 
diff --git a/gcc/predict.c b/gcc/predict.c
index 915f0806b11..3f56fa3a74a 100644
--- a/gcc/predict.c
+++ b/gcc/predict.c
@@ -762,7 +762,6 @@ dump_prediction (FILE *file, enum br_predictor predictor, int probability,
       && bb->count.precise_p ()
       && reason == REASON_NONE)
     {
-      gcc_assert (e->count ().precise_p ());
       fprintf (file, ";;heuristics;%s;%" PRId64 ";%" PRId64 ";%.1f;\n",
 	       predictor_info[predictor].name,
 	       bb->count.to_gcov_type (), e->count ().to_gcov_type (),
diff --git a/gcc/symtab.c b/gcc/symtab.c
index ee9723c3453..d4c36fd3e5a 100644
--- a/gcc/symtab.c
+++ b/gcc/symtab.c
@@ -603,6 +603,7 @@ symtab_node::create_reference (symtab_node *referred_node,
   ref->referred = referred_node;
   ref->stmt = stmt;
   ref->lto_stmt_uid = 0;
+  ref->speculative_id = 0;
   ref->use = use_type;
   ref->speculative = 0;
 
@@ -660,6 +661,7 @@ symtab_node::clone_references (symtab_node *node)
       ref2 = create_reference (ref->referred, ref->use, ref->stmt);
       ref2->speculative = speculative;
       ref2->lto_stmt_uid = stmt_uid;
+      ref2->speculative_id = ref->speculative_id;
     }
 }
 
@@ -678,6 +680,7 @@ symtab_node::clone_referring (symtab_node *node)
       ref2 = ref->referring->create_reference (this, ref->use, ref->stmt);
       ref2->speculative = speculative;
       ref2->lto_stmt_uid = stmt_uid;
+      ref2->speculative_id = ref->speculative_id;
     }
 }
 
@@ -693,6 +696,7 @@ symtab_node::clone_reference (ipa_ref *ref, gimple *stmt)
   ref2 = create_reference (ref->referred, ref->use, stmt);
   ref2->speculative = speculative;
   ref2->lto_stmt_uid = stmt_uid;
+  ref2->speculative_id = ref->speculative_id;
   return ref2;
 }
 
@@ -747,6 +751,7 @@ symtab_node::clear_stmts_in_references (void)
       {
 	r->stmt = NULL;
 	r->lto_stmt_uid = 0;
+	r->speculative_id = 0;
       }
 }
 
diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
new file mode 100644
index 00000000000..e0a83c2e067
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
@@ -0,0 +1,35 @@
+/* { dg-require-effective-target lto } */
+/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate --param indir-call-topn-profile=1" } */
+
+#include <stdio.h>
+
+typedef int (*fptr) (int);
+int
+one (int a);
+
+int
+two (int a);
+
+fptr table[] = {&one, &two};
+
+int
+main()
+{
+  int i, x;
+  fptr p = &one;
+
+  x = one (3);
+
+  for (i = 0; i < 350000000; i++)
+    {
+      x = (*p) (3);
+      p = table[x];
+    }
+  printf ("done:%d\n", x);
+}
+
+/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* one transformation on insn" "profile_estimate" } } */
+/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* two transformation on insn" "profile_estimate" } } */
+
diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
new file mode 100644
index 00000000000..a8c6e365fb9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
@@ -0,0 +1,22 @@
+/* It seems there is no way to avoid the other source of mulitple
+   source testcase from being compiled independently.  Just avoid
+   error.  */
+#ifdef DOJOB
+int
+one (int a)
+{
+  return 1;
+}
+
+int
+two (int a)
+{
+  return 0;
+}
+#else
+int
+main()
+{
+  return 0;
+}
+#endif
diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
new file mode 100644
index 00000000000..aa3887fde83
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
@@ -0,0 +1,42 @@
+/* { dg-require-effective-target lto } */
+/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate --param indir-call-topn-profile=1" } */
+
+#include <stdio.h>
+
+typedef int (*fptr) (int);
+int
+one (int a);
+
+int
+two (int a);
+
+fptr table[] = {&one, &two};
+
+int foo ()
+{
+  int i, x;
+  fptr p = &one;
+
+  x = one (3);
+
+  for (i = 0; i < 350000000; i++)
+    {
+      x = (*p) (3);
+      p = table[x];
+    }
+  return x;
+}
+
+int
+main()
+{
+  int x = foo ();
+  printf ("done:%d\n", x);
+}
+
+/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* one transformation on insn" "profile_estimate" } } */
+/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* two transformation on insn" "profile_estimate" } } */
+
+
diff --git a/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
new file mode 100644
index 00000000000..951bc7ddd19
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
@@ -0,0 +1,38 @@
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-options "-O2 -fdump-ipa-profile --param indir-call-topn-profile=1" } */
+
+#include <stdio.h>
+
+typedef int (*fptr) (int);
+int
+one (int a)
+{
+  return 1;
+}
+
+int
+two (int a)
+{
+  return 0;
+}
+
+fptr table[] = {&one, &two};
+
+int
+main()
+{
+  int i, x;
+  fptr p = &one;
+
+  one (3);
+
+  for (i = 0; i < 350000000; i++)
+    {
+      x = (*p) (3);
+      p = table[x];
+    }
+  printf ("done:%d\n", x);
+}
+
+/* { dg-final-use-not-autofdo { scan-ipa-dump "Indirect call -> direct call.* one transformation on insn" "profile" } } */
+/* { dg-final-use-not-autofdo { scan-ipa-dump "Indirect call -> direct call.* two transformation on insn" "profile" } } */
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index b9c1a3b1456..fe3e172fbd1 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -2167,6 +2167,25 @@ copy_bb (copy_body_data *id, basic_block bb,
 
 			  gcc_assert (!edge->indirect_unknown_callee);
 			  old_edge->speculative_call_info (direct, indirect, ref);
+			  while (old_edge->next_callee
+				 && old_edge->next_callee->speculative
+				 && indirect->has_multiple_indirect_call_p ())
+			    {
+			      /* Some speculative calls may contain more than
+				 one direct target, loop iterate it to clone all
+				 related direct edges before cloning the related
+				 indirect edge.  */
+			      id->dst_node->clone_reference (ref, stmt);
+
+			      edge = old_edge->next_callee;
+			      edge = edge->clone (id->dst_node, call_stmt,
+						  gimple_uid (stmt), num, den,
+						  true);
+			      old_edge = old_edge->next_callee;
+			      gcc_assert (!edge->indirect_unknown_callee);
+			      old_edge->speculative_call_info (direct, indirect,
+							       ref);
+			    }
 
 			  profile_count indir_cnt = indirect->count;
 			  indirect = indirect->clone (id->dst_node, call_stmt,
diff --git a/gcc/tree-profile.c b/gcc/tree-profile.c
index 4c1ead5781f..ef7748668f8 100644
--- a/gcc/tree-profile.c
+++ b/gcc/tree-profile.c
@@ -74,8 +74,8 @@ static GTY(()) tree ic_tuple_callee_field;
 /* Do initialization work for the edge profiler.  */
 
 /* Add code:
-   __thread gcov*	__gcov_indirect_call_counters; // pointer to actual counter
-   __thread void*	__gcov_indirect_call_callee; // actual callee address
+   __thread gcov*	__gcov_indirect_call.counters; // pointer to actual counter
+   __thread void*	__gcov_indirect_call.callee; // actual callee address
    __thread int __gcov_function_counter; // time profiler function counter
 */
 static void
@@ -382,7 +382,7 @@ gimple_gen_ic_profiler (histogram_value value, unsigned tag)
       f_1 = foo;
       __gcov_indirect_call.counters = &__gcov4.main[0];
       PROF_9 = f_1;
-      __gcov_indirect_call_callee = PROF_9;
+      __gcov_indirect_call.callee = PROF_9;
       _4 = f_1 ();
    */
 
@@ -445,11 +445,11 @@ gimple_gen_ic_func_profiler (void)
 
   /* Insert code:
 
-     if (__gcov_indirect_call_callee != NULL)
+     if (__gcov_indirect_call.callee != NULL)
        __gcov_indirect_call_profiler_v3 (profile_id, &current_function_decl);
 
      The function __gcov_indirect_call_profiler_v3 is responsible for
-     resetting __gcov_indirect_call_callee to NULL.  */
+     resetting __gcov_indirect_call.callee to NULL.  */
 
   gimple_stmt_iterator gsi = gsi_start_bb (cond_bb);
   void0 = build_int_cst (ptr_type_node, 0);
@@ -891,7 +891,7 @@ pass_ipa_tree_profile::gate (function *)
 {
   /* When profile instrumentation, use or test coverage shall be performed.
      But for AutoFDO, this there is no instrumentation, thus this pass is
-     diabled.  */
+     disabled.  */
   return (!in_lto_p && !flag_auto_profile
 	  && (flag_branch_probabilities || flag_test_coverage
 	      || profile_arc_flag));
diff --git a/gcc/value-prof.c b/gcc/value-prof.c
index 55ea0973a03..0588df0fce9 100644
--- a/gcc/value-prof.c
+++ b/gcc/value-prof.c
@@ -1406,11 +1406,10 @@ gimple_ic (gcall *icall_stmt, struct cgraph_node *direct_call,
   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:
- */
+/* There maybe multiple indirect targets in histogram.  Check every
+   indirect/virtual call if callee function exists, if not exist, leave it to
+   LTO stage for later process.  Modify code of this indirect call to an if-else
+   structure in ipa-profile finally.  */
 
 static bool
 gimple_ic_transform (gimple_stmt_iterator *gsi)
@@ -1434,48 +1433,57 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
   if (!histogram)
     return false;
 
-  if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
-				  &count, &all))
-    return false;
+  count = 0;
+  all = histogram->hvalue.counters[0];
 
-  if (4 * count <= 3 * all)
-    return false;
+  for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
+    {
+      if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
+				      &count, &all, j))
+	continue;
 
-  direct_call = find_func_by_profile_id ((int)val);
+      /* Minimum probability.  should be higher than 25%.  */
+      if (4 * count <= all)
+	continue;
 
-  if (direct_call == NULL)
-    {
-      if (val)
+      direct_call = find_func_by_profile_id ((int) val);
+
+      if (direct_call == NULL)
+	{
+	  if (val)
+	    {
+	      if (dump_enabled_p ())
+		dump_printf_loc (
+		  MSG_MISSED_OPTIMIZATION, stmt,
+		  "Indirect call -> direct call from other "
+		  "module %T=> %i (will resolve only with LTO)\n",
+		  gimple_call_fn (stmt), (int) val);
+	    }
+	  continue;
+	}
+
+      if (!check_ic_target (stmt, direct_call))
 	{
 	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
-			     "Indirect call -> direct call from other "
-			     "module %T=> %i (will resolve only with LTO)\n",
-			     gimple_call_fn (stmt), (int)val);
+	    dump_printf_loc (
+	      MSG_MISSED_OPTIMIZATION, stmt,
+	      "Indirect call -> direct call %T => %T "
+	      "transformation skipped because of type mismatch: %G",
+	      gimple_call_fn (stmt), direct_call->decl, stmt);
+	  gimple_remove_histogram_value (cfun, stmt, histogram);
+	  return false;
 	}
-      return false;
-    }
 
-  if (!check_ic_target (stmt, direct_call))
-    {
       if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
-			 "Indirect call -> direct call %T => %T "
-			 "transformation skipped because of type mismatch: %G",
-			 gimple_call_fn (stmt), direct_call->decl, stmt);
-      gimple_remove_histogram_value (cfun, stmt, histogram);
-      return false;
-    }
-
-  if (dump_enabled_p ())
-    {
-      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
-		       "Indirect call -> direct call "
-		       "%T => %T transformation on insn postponed\n",
-		       gimple_call_fn (stmt), direct_call->decl);
-      dump_printf_loc (MSG_NOTE, stmt,
-		       "hist->count %" PRId64
-		       " hist->all %" PRId64"\n", count, all);
+	{
+	  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
+			   "Indirect call -> direct call "
+			   "%T => %T transformation on insn postponed\n",
+			   gimple_call_fn (stmt), direct_call->decl);
+	  dump_printf_loc (MSG_NOTE, stmt,
+			   "hist->count %" PRId64 " hist->all %" PRId64 "\n",
+			   count, all);
+	}
     }
 
   return true;
-- 
2.21.0.777.g83232e3864



^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v4] Missed function specialization + partial devirtualization
  2019-09-25  3:46       ` [PATCH v4] " luoxhu
@ 2019-09-25 10:57         ` Martin Liška
  2019-09-26  5:24           ` luoxhu
  0 siblings, 1 reply; 38+ messages in thread
From: Martin Liška @ 2019-09-25 10:57 UTC (permalink / raw)
  To: luoxhu, gcc-patches; +Cc: hubicka, segher, wschmidt

[-- Attachment #1: Type: text/plain, Size: 44827 bytes --]

On 9/25/19 5:45 AM, luoxhu wrote:
> Hi,
> 
> Sorry for replying so late due to cauldron conference and other LTO issues
> I was working on.

Hello.

That's fine, we still have plenty of time for patch review.

Not fixed issues which I reported in v3 (and still valid in v4):
- please come up with indirect_target_info::indirect_target_info and use it
- do you need to stream out indirect_call_targets when common_target_id == 0?

Then I'm suggesting to use vec::is_empty (please see my patch).

I see following failures for the tests provided:
FAIL: gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c compilation,  -fprofile-generate -D_PROFILE_GENERATE
FAIL: gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c compilation,  -fprofile-generate -D_PROFILE_GENERATE
FAIL: gcc.dg/tree-prof/indir-call-prof-topn.c compilation,  -fprofile-generate -D_PROFILE_GENERATE

Next comments follow directly in the email body:

> 
> v4 Changes:
>  1. Rebase to trunk.
>  2. Remove num_of_ics and use vector's length to avoid redundancy.
>  3. Update the code in ipa-profile.c to improve review feasibility.
>  4. Add function has_indirect_call_p and has_multiple_indirect_call_p.
>  5. For parameter control, I will leave it to next patch as it is a
>     relative independent function.  Currently, maximum number of
>     promotions is GCOV_TOPN_VALUES as only 4 profiling value limited
>     from profile-generate, therefore minimum probability is adjusted to
>     25% in value-prof.c, it was 75% also by hard code for single
>     indirect target.  No control to minimal number of edge
>     executions yet.  What's more, this patch is a bit large now.
> 
> This patch aims to fix PR69678 caused by PGO indirect call profiling
> performance issues.
> The bug that profiling data is never working was fixed by Martin's pull
> back of topN patches, performance got GEOMEAN ~1% improvement(+24% for
> 511.povray_r specifically).
> Still, currently the default profile only generates SINGLE indirect target
> that called more than 75%.  This patch leverages MULTIPLE indirect
> targets use in LTO-WPA and LTO-LTRANS stage, as a result, function
> specialization, profiling, partial devirtualization, inlining and
> cloning could be done successfully based on it.
> Performance can get improved from 0.70 sec to 0.38 sec on simple tests.
> Details are:
>   1.  PGO with topn is enabled by default now, but only one indirect
>   target edge will be generated in ipa-profile pass, so add variables to enable
>   multiple speculative edges through passes, speculative_id will record the
>   direct edge index bind to the indirect edge, indirect_call_targets length
>   records how many direct edges owned by the indirect edge, postpone gimple_ic
>   to ipa-profile like default as inline pass will decide whether it is benefit
>   to transform indirect call.
>   2.  Use speculative_id to track and search the reference node matched
>   with the direct edge's callee for multiple targets.  Actually, it is the
>   caller's responsibility to handle the direct edges mapped to same indirect
>   edge.  speculative_call_info will return one of the direct edge specified,
>   this will leverage current IPA edge process framework mostly.
>   3.  Enable LTO WPA/LTRANS stage multiple indirect call targets analysis for
>   profile full support in ipa passes and cgraph_edge functions.  speculative_id
>   can be set by make_speculative id when multiple targets are binded to
>   one indirect edge, and cloned if new edge is cloned.  speculative_id
>   is streamed out and stream int by lto like lto_stmt_uid.
>   4.  Add 1 in module testcase and 2 cross module testcases.
>   5.  Bootstrap and regression test passed on Power8-LE.  No function
>   and performance regression for SPEC2017.
> 
> gcc/ChangeLog
> 
> 	2019-09-25  Xiong Hu Luo  <luoxhu@linux.ibm.com>
> 
> 	PR ipa/69678
> 	* cgraph.c (symbol_table::create_edge): Init speculative_id.
> 	(cgraph_edge::make_speculative): Add param for setting speculative_id.
> 	(cgraph_edge::speculative_call_info): Find reference by
> 	speculative_id for multiple indirect targets.
> 	(cgraph_edge::resolve_speculation): Decrease the speculations
> 	for indirect edge, drop it's speculative if not direct target
> 	left.
> 	(cgraph_edge::redirect_call_stmt_to_callee): Likewise.
> 	(cgraph_node::verify_node): Don't report error if speculative
> 	edge not include statement.
> 	(cgraph_edge::has_multiple_indirect_call_p): New function.
> 	(cgraph_edge::has_indirect_call_p): New function.
> 	* cgraph.h (struct indirect_target_info): New struct.
> 	(indirect_call_targets): New vector variable.
> 	(make_speculative): Add param for setting speculative_id.
> 	(cgraph_edge::has_multiple_indirect_call_p): New declare.
> 	(cgraph_edge::has_indirect_call_p): New declare.
> 	(speculative_id): New variable.
> 	* cgraphclones.c (cgraph_node::create_clone): Clone speculative_id.
> 	* ipa-inline.c (inline_small_functions): Fix iterator update.
> 	* ipa-profile.c (ipa_profile_generate_summary): Add indirect
> 	multiple targets logic.
> 	(ipa_profile): Likewise.
> 	* ipa-ref.h (speculative_id): New variable.
> 	* ipa.c (process_references): Fix typo.
> 	* lto-cgraph.c (lto_output_edge): Add indirect multiple targets
> 	logic.  Stream out speculative_id.
> 	(input_edge): Likewise.
> 	* predict.c (dump_prediction): Remove edges count assert to be
> 	precise.
> 	* symtab.c (symtab_node::create_reference): Init speculative_id.
> 	(symtab_node::clone_references): Clone speculative_id.
> 	(symtab_node::clone_referring): Clone speculative_id.
> 	(symtab_node::clone_reference): Clone speculative_id.
> 	(symtab_node::clear_stmts_in_references): Clear speculative_id.
> 	* tree-inline.c (copy_bb): Duplicate all the speculative edges
> 	if indirect call contains multiple speculative targets.
> 	* tree-profile.c (gimple_gen_ic_profiler): Use the new variable
> 	__gcov_indirect_call.counters and __gcov_indirect_call.callee.
> 	(gimple_gen_ic_func_profiler): Likewise.
> 	(pass_ipa_tree_profile::gate): Fix comment typos.
> 	* value-prof.c  (gimple_ic_transform): Handle topn case.
> 	Fix comment typos.
> 
> gcc/testsuite/ChangeLog
> 
> 	2019-09-25  Xiong Hu Luo  <luoxhu@linux.ibm.com>
> 
> 	PR ipa/69678
> 	* gcc.dg/tree-prof/indir-call-prof-topn.c: New testcase.
> 	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c: New testcase.
> 	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c: New testcase.
> 	* gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c: New testcase.
> ---
>  gcc/cgraph.c                                  | 90 ++++++++++++++++-
>  gcc/cgraph.h                                  | 29 +++++-
>  gcc/cgraphclones.c                            |  1 +
>  gcc/ipa-inline.c                              | 15 +--
>  gcc/ipa-profile.c                             | 96 ++++++++++++++-----
>  gcc/ipa-ref.h                                 |  1 +
>  gcc/ipa.c                                     |  2 +-
>  gcc/lto-cgraph.c                              | 57 +++++++++--
>  gcc/predict.c                                 |  1 -
>  gcc/symtab.c                                  |  5 +
>  .../tree-prof/crossmodule-indir-call-topn-1.c | 35 +++++++
>  .../crossmodule-indir-call-topn-1a.c          | 22 +++++
>  .../tree-prof/crossmodule-indir-call-topn-2.c | 42 ++++++++
>  .../gcc.dg/tree-prof/indir-call-prof-topn.c   | 38 ++++++++
>  gcc/tree-inline.c                             | 19 ++++
>  gcc/tree-profile.c                            | 12 +--
>  gcc/value-prof.c                              | 86 +++++++++--------
>  17 files changed, 452 insertions(+), 99 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
>  create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
>  create mode 100644 gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
> 
> diff --git a/gcc/cgraph.c b/gcc/cgraph.c
> index 843891e9e56..9a28aca435f 100644
> --- a/gcc/cgraph.c
> +++ b/gcc/cgraph.c
> @@ -860,6 +860,7 @@ symbol_table::create_edge (cgraph_node *caller, cgraph_node *callee,
>    edge->prev_callee = NULL;
>    edge->next_callee = NULL;
>    edge->lto_stmt_uid = 0;
> +  edge->speculative_id = 0;
>  
>    edge->count = count;
>  
> @@ -1051,7 +1052,8 @@ cgraph_edge::remove (void)
>     Return direct edge created.  */
>  
>  cgraph_edge *
> -cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
> +cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count,
> +			       unsigned int speculative_id)
>  {
>    cgraph_node *n = caller;
>    ipa_ref *ref = NULL;
> @@ -1069,11 +1071,13 @@ cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
>    else
>      e2->can_throw_external = can_throw_external;
>    e2->lto_stmt_uid = lto_stmt_uid;
> +  e2->speculative_id = speculative_id;
>    e2->in_polymorphic_cdtor = in_polymorphic_cdtor;
>    count -= e2->count;
>    symtab->call_edge_duplication_hooks (this, e2);
>    ref = n->create_reference (n2, IPA_REF_ADDR, call_stmt);
>    ref->lto_stmt_uid = lto_stmt_uid;
> +  ref->speculative_id = speculative_id;
>    ref->speculative = speculative;
>    n2->mark_address_taken ();
>    return e2;
> @@ -1087,6 +1091,38 @@ cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
>     call) and if one of them exists, all of them must exist.
>  
>     Given speculative call edge, return all three components.
> +
> +   For some indirect edge, it may maps to multiple direct edges, i.e.  1:N.
> +   check the speculative_id to return all the three components for specified
> +   direct edge or indirect edge.
> +   If input is indirect, caller of this function will get the direct edge one by
> +   one, get_edge will just return one of the direct edge mapped to the indirect
> +   edge, the returned direct edge will be resolved or redirected by the caller,
> +   then number of indirect calls (speculations) is deceased in each access.
> +   If input is direct, this function will get the indirect edge and reference
> +   with matched speculative_id, the returned edge will also be resolved or
> +   redirected, decrease the speculations accordingly.
> +   Speculations of indirect edge will be dropped only if all direct edges
> +   be handled.
> +
> +   e.g.  for indirect edge E statement "call call_dest":
> +
> +   Redirect N3 after redirected N2:
> +
> +   if (call_dest == N2)
> +     n2 ();
> +   else if (call_dest == N3)
> +     n3 ();
> +   else
> +     call call_dest
> +
> +   Resolve N3 and only redirect N2:
> +
> +   if (call_dest == N2)
> +     n2 ();
> +   else
> +     call call_dest
> +
>   */
>  
>  void
> @@ -1126,7 +1162,7 @@ cgraph_edge::speculative_call_info (cgraph_edge *&direct,
>  
>    reference = NULL;
>    for (i = 0; e->caller->iterate_reference (i, ref); i++)
> -    if (ref->speculative
> +    if (ref->speculative && ref->speculative_id == e->speculative_id
>  	&& ((ref->stmt && ref->stmt == e->call_stmt)
>  	    || (!ref->stmt && ref->lto_stmt_uid == e->lto_stmt_uid)))
>        {
> @@ -1187,7 +1223,21 @@ cgraph_edge::resolve_speculation (tree callee_decl)
>           in the functions inlined through it.  */
>      }
>    edge->count += e2->count;
> -  edge->speculative = false;
> +  /* edge is indirect, e2 is direct.  If edge contains multiple speculations,
> +     remove one of speculations for this indirect edge, then if edge still
> +     contains direct target, keep the speculation, next direct target
> +     will continue use it.  Give up speculation completely if no direct
> +     target is left for this indirect edge.  */
> +  if (edge->has_indirect_call_p ())
> +    {
> +      /* As the direct targets are sorted by decrease, delete the first target
> +	 when it is resolved.  */
> +      edge->indirect_info->indirect_call_targets->ordered_remove (0);
> +      if (!edge->indirect_info->indirect_call_targets->length ())
> +	edge->speculative = false;
> +    }
> +  else
> +    edge->speculative = false;
>    e2->speculative = false;
>    ref->remove_reference ();
>    if (e2->indirect_unknown_callee || e2->inline_failed)
> @@ -1321,7 +1371,21 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
>  	  e->caller->set_call_stmt_including_clones (e->call_stmt, new_stmt,
>  						     false);
>  	  e->count = gimple_bb (e->call_stmt)->count;
> -	  e2->speculative = false;
> +	  /* edge is direct, e2 is indirect here.  If e2 contains multiple
> +	     speculations, remove one of speculations for this indirect edge,
> +	     then if e2 still contains direct target, keep the speculation,
> +	     next direct target will continue use it.  Give up speculation
> +	     completely if no direct target is left for this indirect e2.  */
> +	  if (e2->has_indirect_call_p ())
> +	    {
> +	      /* As the direct targets are sorted by decrease, delete the first
> +		 target when it is redirected.  */
> +	      e2->indirect_info->indirect_call_targets->ordered_remove (0);
> +	      if (!e2->indirect_info->indirect_call_targets->length ())
> +		e2->speculative = false;
> +	    }
> +	  else
> +	    e2->speculative = false;
>  	  e2->count = gimple_bb (e2->call_stmt)->count;
>  	  ref->speculative = false;
>  	  ref->stmt = NULL;
> @@ -3445,7 +3509,7 @@ cgraph_node::verify_node (void)
>  
>        for (e = callees; e; e = e->next_callee)
>  	{
> -	  if (!e->aux)
> +	  if (!e->aux && !e->speculative)
>  	    {
>  	      error ("edge %s->%s has no corresponding call_stmt",
>  		     identifier_to_locale (e->caller->name ()),
> @@ -3872,6 +3936,22 @@ cgraph_edge::possibly_call_in_translation_unit_p (void)
>    return node->get_availability () >= AVAIL_AVAILABLE;
>  }
>  
> +/* Return true if this edge has multiple indirect call targets.  */
> +bool
> +cgraph_edge::has_multiple_indirect_call_p (void)
> +{
> +  return indirect_info && indirect_info->indirect_call_targets
> +	 && indirect_info->indirect_call_targets->length () > 1;
> +}

For multiline && expression, we typically wrap the whole condition
in '(' and ')'.

> +
> +/* Return true if this edge has at least one indirect call target.  */
> +bool
> +cgraph_edge::has_indirect_call_p (void)
> +{
> +  return indirect_info && indirect_info->indirect_call_targets
> +	 && indirect_info->indirect_call_targets->length ();
> +}

Likewise here.

> +
>  /* A stashed copy of "symtab" for use by selftest::symbol_table_test.
>     This needs to be a global so that it can be a GC root, and thus
>     prevent the stashed copy from being garbage-collected if the GC runs
> diff --git a/gcc/cgraph.h b/gcc/cgraph.h
> index 4c54210123a..33c8454c4e0 100644
> --- a/gcc/cgraph.h
> +++ b/gcc/cgraph.h
> @@ -1636,6 +1636,16 @@ private:
>    void make_speculative (tree otr_type = NULL);
>  };
>  
> +/* Structure containing indirect target information from profile.  */
> +
> +struct GTY (()) indirect_target_info
> +{
> +  /* Profile_id of common target obtained from profile.  */
> +  unsigned int common_target_id;
> +  /* Probability that call will land in function with COMMON_TARGET_ID.  */
> +  int common_target_probability;
> +};
> +
>  /* Structure containing additional information about an indirect call.  */
>  
>  class GTY(()) cgraph_indirect_call_info
> @@ -1654,10 +1664,9 @@ public:
>    int param_index;
>    /* ECF flags determined from the caller.  */
>    int ecf_flags;
> -  /* Profile_id of common target obtrained from profile.  */
> -  int common_target_id;
> -  /* Probability that call will land in function with COMMON_TARGET_ID.  */
> -  int common_target_probability;
> +
> +  /* An indirect call may contain one or multiple call targets.  */
> +  vec<indirect_target_info, va_gc> *indirect_call_targets;
>  
>    /* Set when the call is a virtual call with the parameter being the
>       associated object pointer rather than a simple direct call.  */
> @@ -1714,7 +1723,8 @@ public:
>    /* Turn edge into speculative call calling N2. Update
>       the profile so the direct call is taken COUNT times
>       with FREQUENCY.  */
> -  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count);
> +  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count,
> +				 unsigned int speculative_id = 0);
>  
>     /* Given speculative call edge, return all three components.  */
>    void speculative_call_info (cgraph_edge *&direct, cgraph_edge *&indirect,
> @@ -1773,6 +1783,12 @@ public:
>       be internal to the current translation unit.  */
>    bool possibly_call_in_translation_unit_p (void);
>  
> +  /* Return true if this edge has multiple indirect call targets.  */
> +  bool has_multiple_indirect_call_p (void);
> +
> +  /* Return true if this edge has at least one indirect call target.  */
> +  bool has_indirect_call_p (void);
> +
>    /* Expected number of executions: calculated in profile.c.  */
>    profile_count count;
>    cgraph_node *caller;
> @@ -1792,6 +1808,9 @@ public:
>    /* The stmt_uid of call_stmt.  This is used by LTO to recover the call_stmt
>       when the function is serialized in.  */
>    unsigned int lto_stmt_uid;
> +  /* speculative id is used by multiple indirect targets when the function is
> +   speculated.  */
> +  unsigned int speculative_id;
>    /* Whether this edge was made direct by indirect inlining.  */
>    unsigned int indirect_inlining_edge : 1;
>    /* Whether this edge describes an indirect call with an undetermined
> diff --git a/gcc/cgraphclones.c b/gcc/cgraphclones.c
> index fa753697c78..5dbd8d90b77 100644
> --- a/gcc/cgraphclones.c
> +++ b/gcc/cgraphclones.c
> @@ -128,6 +128,7 @@ cgraph_edge::clone (cgraph_node *n, gcall *call_stmt, unsigned stmt_uid,
>    new_edge->inline_failed = inline_failed;
>    new_edge->indirect_inlining_edge = indirect_inlining_edge;
>    new_edge->lto_stmt_uid = stmt_uid;
> +  new_edge->speculative_id = speculative_id;
>    /* Clone flags that depend on call_stmt availability manually.  */
>    new_edge->can_throw_external = can_throw_external;
>    new_edge->call_stmt_cannot_inline_p = call_stmt_cannot_inline_p;
> diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
> index b62d280eb25..6136214f9ac 100644
> --- a/gcc/ipa-inline.c
> +++ b/gcc/ipa-inline.c
> @@ -1881,12 +1881,15 @@ inline_small_functions (void)
>  	}
>        if (has_speculative)
>  	for (edge = node->callees; edge; edge = next)
> -	  if (edge->speculative && !speculation_useful_p (edge,
> -							  edge->aux != NULL))
> -	    {
> -	      edge->resolve_speculation ();
> -	      update = true;
> -	    }
> +	  {
> +	    next = edge->next_callee;
> +	    if (edge->speculative
> +		&& !speculation_useful_p (edge, edge->aux != NULL))
> +	      {
> +		edge->resolve_speculation ();
> +		update = true;
> +	      }
> +	  }
>        if (update)
>  	{
>  	  struct cgraph_node *where = node->global.inlined_to
> diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
> index 970dba39c80..342e8ea05d1 100644
> --- a/gcc/ipa-profile.c
> +++ b/gcc/ipa-profile.c
> @@ -192,23 +192,35 @@ ipa_profile_generate_summary (void)
>  		  if (h)
>  		    {
>  		      gcov_type val, count, all;
> -		      if (get_nth_most_common_value (NULL, "indirect call", h,
> -						     &val, &count, &all))
> +		      struct cgraph_edge *e = node->get_edge (stmt);
> +		      if (e && !e->indirect_unknown_callee)
> +			continue;
> +
> +		      struct indirect_target_info item;
> +		      for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
>  			{
> -			  struct cgraph_edge * e = node->get_edge (stmt);
> -			  if (e && !e->indirect_unknown_callee)
> +			  if (!get_nth_most_common_value (NULL, "indirect call",
> +							  h, &val, &count, &all,
> +							  j))
> +			    continue;
> +
> +			  if (val == 0)
>  			    continue;
>  
> -			  e->indirect_info->common_target_id = val;
> -			  e->indirect_info->common_target_probability
> +			  item.common_target_id = val;
> +			  item.common_target_probability
>  			    = GCOV_COMPUTE_SCALE (count, all);

There's one of the places where you can use the constructor.

> -			  if (e->indirect_info->common_target_probability > REG_BR_PROB_BASE)
> +			  if (item.common_target_probability > REG_BR_PROB_BASE)
>  			    {
>  			      if (dump_file)
> -				fprintf (dump_file, "Probability capped to 1\n");
> -			      e->indirect_info->common_target_probability = REG_BR_PROB_BASE;
> +				fprintf (dump_file,
> +					 "Probability capped to 1\n");
> +			      item.common_target_probability = REG_BR_PROB_BASE;
>  			    }
> +			  vec_safe_push (
> +			    e->indirect_info->indirect_call_targets, item);
>  			}
> +
>  		      gimple_remove_histogram_value (DECL_STRUCT_FUNCTION (node->decl),
>  						      stmt, h);
>  		    }
> @@ -492,6 +504,7 @@ ipa_profile (void)
>    int nindirect = 0, ncommon = 0, nunknown = 0, nuseless = 0, nconverted = 0;
>    int nmismatch = 0, nimpossible = 0;
>    bool node_map_initialized = false;
> +  gcov_type threshold;
>  
>    if (dump_file)
>      dump_histogram (dump_file, histogram);
> @@ -500,14 +513,12 @@ ipa_profile (void)
>        overall_time += histogram[i]->count * histogram[i]->time;
>        overall_size += histogram[i]->size;
>      }
> +  threshold = 0;
>    if (overall_time)
>      {
> -      gcov_type threshold;
> -
>        gcc_assert (overall_size);
>  
>        cutoff = (overall_time * PARAM_VALUE (HOT_BB_COUNT_WS_PERMILLE) + 500) / 1000;
> -      threshold = 0;
>        for (i = 0; cumulated < cutoff; i++)
>  	{
>  	  cumulated += histogram[i]->count * histogram[i]->time;
> @@ -543,7 +554,7 @@ ipa_profile (void)
>    histogram.release ();
>    histogram_pool.release ();
>  
> -  /* Produce speculative calls: we saved common traget from porfiling into
> +  /* Produce speculative calls: we saved common target from profiling into
>       e->common_target_id.  Now, at link time, we can look up corresponding
>       function node and produce speculative call.  */
>  
> @@ -558,13 +569,37 @@ ipa_profile (void)
>  	{
>  	  if (n->count.initialized_p ())
>  	    nindirect++;
> -	  if (e->indirect_info->common_target_id)
> +	  if (e->has_indirect_call_p ())
>  	    {
>  	      if (!node_map_initialized)
> -	        init_node_map (false);
> +		init_node_map (false);
>  	      node_map_initialized = true;
>  	      ncommon++;
> -	      n2 = find_func_by_profile_id (e->indirect_info->common_target_id);
> +
> +	      if (in_lto_p)
> +		{
> +		  if (dump_file)
> +		    {
> +		      fprintf (dump_file,
> +			       "Updating hotness threshold in LTO mode.\n");
> +		      fprintf (dump_file, "Updated min count: %" PRId64 "\n",
> +			       (int64_t) threshold);
> +		    }
> +		  set_hot_bb_threshold (threshold
> +		    / e->indirect_info->indirect_call_targets->length ());
> +		}
> +
> +	      unsigned speculative_id = 0;
> +	      struct indirect_target_info *item;
> +	      /* The code below is not formatted yet for review convenience.
> +		 Move to a seprate small function is not easy as too many local
> +		 variables used in it.  Need format and remove this comments
> +		 once got approved.  */
> +	      FOR_EACH_VEC_SAFE_ELT (e->indirect_info->indirect_call_targets, i,
> +				     item)
> +	       {
> +	      bool speculative_found = false;
> +	      n2 = find_func_by_profile_id (item->common_target_id);
>  	      if (n2)
>  		{
>  		  if (dump_file)
> @@ -573,11 +608,10 @@ ipa_profile (void)
>  			       " other module %s => %s, prob %3.2f\n",
>  			       n->dump_name (),
>  			       n2->dump_name (),
> -			       e->indirect_info->common_target_probability
> -			       / (float)REG_BR_PROB_BASE);
> +			       item->common_target_probability
> +				 / (float) REG_BR_PROB_BASE);
>  		    }
> -		  if (e->indirect_info->common_target_probability
> -		      < REG_BR_PROB_BASE / 2)
> +		  if (item->common_target_probability < REG_BR_PROB_BASE / 2)
>  		    {
>  		      nuseless++;
>  		      if (dump_file)
> @@ -613,7 +647,7 @@ ipa_profile (void)
>  		      if (dump_file)
>  			fprintf (dump_file,
>  				 "Not speculating: "
> -				 "parameter count mistmatch\n");
> +				 "parameter count mismatch\n");
>  		    }
>  		  else if (e->indirect_info->polymorphic
>  			   && !opt_for_fn (n->decl, flag_devirtualize)
> @@ -640,20 +674,30 @@ ipa_profile (void)
>  			    n2 = alias;
>  			}
>  		      nconverted++;
> -		      e->make_speculative
> -			(n2,
> -			 e->count.apply_probability
> -				     (e->indirect_info->common_target_probability));
> +		      e->make_speculative (n2,
> +					   e->count.apply_probability (
> +					     item->common_target_probability),
> +					   speculative_id);
>  		      update = true;
> +		      speculative_id++;
> +		      speculative_found = true;
>  		    }
>  		}
>  	      else
>  		{
>  		  if (dump_file)
>  		    fprintf (dump_file, "Function with profile-id %i not found.\n",
> -			     e->indirect_info->common_target_id);
> +			     item->common_target_id);
>  		  nunknown++;
>  		}
> +	      if (!speculative_found)
> +		{
> +		  /* Remove item from indirect_call_targets if no
> +		     speculative edge generated, rollback the iteration.  */
> +		  e->indirect_info->indirect_call_targets->ordered_remove (i);
> +		  i--;
> +		}
> +	       }
>  	    }
>  	 }
>         if (update)
> diff --git a/gcc/ipa-ref.h b/gcc/ipa-ref.h
> index 0d8e509c932..3e6562ec9d1 100644
> --- a/gcc/ipa-ref.h
> +++ b/gcc/ipa-ref.h
> @@ -59,6 +59,7 @@ public:
>    symtab_node *referred;
>    gimple *stmt;
>    unsigned int lto_stmt_uid;
> +  unsigned int speculative_id;
>    unsigned int referred_index;
>    ENUM_BITFIELD (ipa_ref_use) use:3;
>    unsigned int speculative:1;
> diff --git a/gcc/ipa.c b/gcc/ipa.c
> index 6b84e1f9bda..a10b0603f14 100644
> --- a/gcc/ipa.c
> +++ b/gcc/ipa.c
> @@ -166,7 +166,7 @@ process_references (symtab_node *snode,
>     devirtualization happens.  After inlining still keep their declarations
>     around, so we can devirtualize to a direct call.
>  
> -   Also try to make trivial devirutalization when no or only one target is
> +   Also try to make trivial devirtualization when no or only one target is
>     possible.  */
>  
>  static void
> diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
> index bc0f0107333..61380dcc7b8 100644
> --- a/gcc/lto-cgraph.c
> +++ b/gcc/lto-cgraph.c
> @@ -238,6 +238,7 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
>    unsigned int uid;
>    intptr_t ref;
>    struct bitpack_d bp;
> +  unsigned len;
>  
>    if (edge->indirect_unknown_callee)
>      streamer_write_enum (ob->main_stream, LTO_symtab_tags, LTO_symtab_last_tag,
> @@ -265,6 +266,7 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
>    bp_pack_enum (&bp, cgraph_inline_failed_t,
>  	        CIF_N_REASONS, edge->inline_failed);
>    bp_pack_var_len_unsigned (&bp, uid);
> +  bp_pack_var_len_unsigned (&bp, edge->speculative_id);
>    bp_pack_value (&bp, edge->indirect_inlining_edge, 1);
>    bp_pack_value (&bp, edge->speculative, 1);
>    bp_pack_value (&bp, edge->call_stmt_cannot_inline_p, 1);
> @@ -291,11 +293,27 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
>    streamer_write_bitpack (&bp);
>    if (edge->indirect_unknown_callee)
>      {
> -      streamer_write_hwi_stream (ob->main_stream,
> -			         edge->indirect_info->common_target_id);
> -      if (edge->indirect_info->common_target_id)
> -	streamer_write_hwi_stream
> -	   (ob->main_stream, edge->indirect_info->common_target_probability);
> +      struct indirect_target_info *item;
> +      unsigned int i;
> +      len = edge->has_indirect_call_p ()
> +	      ? edge->indirect_info->indirect_call_targets->length ()
> +	      : 0;
> +      gcc_assert (len <= GCOV_TOPN_VALUES);
> +
> +      streamer_write_hwi_stream (ob->main_stream, len);
> +
> +      if (len)
> +	{
> +	  FOR_EACH_VEC_SAFE_ELT (edge->indirect_info->indirect_call_targets, i,
> +				 item)
> +	    {
> +	      streamer_write_hwi_stream (ob->main_stream,
> +					 item->common_target_id);
> +	      if (item->common_target_id)
> +		streamer_write_hwi_stream (ob->main_stream,
> +					   item->common_target_probability);
> +	    }
> +	}
>      }
>  }
>  
> @@ -688,6 +706,7 @@ lto_output_ref (struct lto_simple_output_block *ob, struct ipa_ref *ref,
>        if (ref->stmt)
>  	uid = gimple_uid (ref->stmt) + 1;
>        streamer_write_hwi_stream (ob->main_stream, uid);
> +      streamer_write_hwi_stream (ob->main_stream, ref->speculative_id);
>      }
>  }
>  
> @@ -1419,7 +1438,10 @@ input_ref (class lto_input_block *ib,
>    ref = referring_node->create_reference (node, use);
>    ref->speculative = speculative;
>    if (is_a <cgraph_node *> (referring_node))
> -    ref->lto_stmt_uid = streamer_read_hwi (ib);
> +    {
> +      ref->lto_stmt_uid = streamer_read_hwi (ib);
> +      ref->speculative_id = streamer_read_hwi (ib);
> +    }
>  }
>  
>  /* Read an edge from IB.  NODES points to a vector of previously read nodes for
> @@ -1433,11 +1455,12 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
>  {
>    struct cgraph_node *caller, *callee;
>    struct cgraph_edge *edge;
> -  unsigned int stmt_id;
> +  unsigned int stmt_id, speculative_id;
>    profile_count count;
>    cgraph_inline_failed_t inline_failed;
>    struct bitpack_d bp;
>    int ecf_flags = 0;
> +  unsigned i, len;
>  
>    caller = dyn_cast<cgraph_node *> (nodes[streamer_read_hwi (ib)]);
>    if (caller == NULL || caller->decl == NULL_TREE)
> @@ -1457,6 +1480,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
>    bp = streamer_read_bitpack (ib);
>    inline_failed = bp_unpack_enum (&bp, cgraph_inline_failed_t, CIF_N_REASONS);
>    stmt_id = bp_unpack_var_len_unsigned (&bp);
> +  speculative_id = bp_unpack_var_len_unsigned (&bp);
>  
>    if (indirect)
>      edge = caller->create_indirect_edge (NULL, 0, count);
> @@ -1466,6 +1490,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
>    edge->indirect_inlining_edge = bp_unpack_value (&bp, 1);
>    edge->speculative = bp_unpack_value (&bp, 1);
>    edge->lto_stmt_uid = stmt_id;
> +  edge->speculative_id = speculative_id;
>    edge->inline_failed = inline_failed;
>    edge->call_stmt_cannot_inline_p = bp_unpack_value (&bp, 1);
>    edge->can_throw_external = bp_unpack_value (&bp, 1);
> @@ -1485,9 +1510,21 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
>        if (bp_unpack_value (&bp, 1))
>  	ecf_flags |= ECF_RETURNS_TWICE;
>        edge->indirect_info->ecf_flags = ecf_flags;
> -      edge->indirect_info->common_target_id = streamer_read_hwi (ib);
> -      if (edge->indirect_info->common_target_id)
> -        edge->indirect_info->common_target_probability = streamer_read_hwi (ib);
> +
> +      len = streamer_read_hwi (ib);
> +
> +      gcc_assert (len <= GCOV_TOPN_VALUES);
> +
> +      if (len)
> +	{
> +	  indirect_target_info item;
> +	  for (i = 0; i < len; i++)
> +	    {
> +	      item.common_target_id = streamer_read_hwi (ib);
> +	      item.common_target_probability = streamer_read_hwi (ib);
> +	      vec_safe_push (edge->indirect_info->indirect_call_targets, item);
> +	    }
> +	}
>      }
>  }
>  
> diff --git a/gcc/predict.c b/gcc/predict.c
> index 915f0806b11..3f56fa3a74a 100644
> --- a/gcc/predict.c
> +++ b/gcc/predict.c
> @@ -762,7 +762,6 @@ dump_prediction (FILE *file, enum br_predictor predictor, int probability,
>        && bb->count.precise_p ()
>        && reason == REASON_NONE)
>      {
> -      gcc_assert (e->count ().precise_p ());
>        fprintf (file, ";;heuristics;%s;%" PRId64 ";%" PRId64 ";%.1f;\n",
>  	       predictor_info[predictor].name,
>  	       bb->count.to_gcov_type (), e->count ().to_gcov_type (),
> diff --git a/gcc/symtab.c b/gcc/symtab.c
> index ee9723c3453..d4c36fd3e5a 100644
> --- a/gcc/symtab.c
> +++ b/gcc/symtab.c
> @@ -603,6 +603,7 @@ symtab_node::create_reference (symtab_node *referred_node,
>    ref->referred = referred_node;
>    ref->stmt = stmt;
>    ref->lto_stmt_uid = 0;
> +  ref->speculative_id = 0;
>    ref->use = use_type;
>    ref->speculative = 0;
>  
> @@ -660,6 +661,7 @@ symtab_node::clone_references (symtab_node *node)
>        ref2 = create_reference (ref->referred, ref->use, ref->stmt);
>        ref2->speculative = speculative;
>        ref2->lto_stmt_uid = stmt_uid;
> +      ref2->speculative_id = ref->speculative_id;
>      }
>  }
>  
> @@ -678,6 +680,7 @@ symtab_node::clone_referring (symtab_node *node)
>        ref2 = ref->referring->create_reference (this, ref->use, ref->stmt);
>        ref2->speculative = speculative;
>        ref2->lto_stmt_uid = stmt_uid;
> +      ref2->speculative_id = ref->speculative_id;
>      }
>  }
>  
> @@ -693,6 +696,7 @@ symtab_node::clone_reference (ipa_ref *ref, gimple *stmt)
>    ref2 = create_reference (ref->referred, ref->use, stmt);
>    ref2->speculative = speculative;
>    ref2->lto_stmt_uid = stmt_uid;
> +  ref2->speculative_id = ref->speculative_id;
>    return ref2;
>  }
>  
> @@ -747,6 +751,7 @@ symtab_node::clear_stmts_in_references (void)
>        {
>  	r->stmt = NULL;
>  	r->lto_stmt_uid = 0;
> +	r->speculative_id = 0;
>        }
>  }
>  
> diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
> new file mode 100644
> index 00000000000..e0a83c2e067
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
> @@ -0,0 +1,35 @@
> +/* { dg-require-effective-target lto } */
> +/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
> +/* { dg-require-profiling "-fprofile-generate" } */
> +/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate --param indir-call-topn-profile=1" } */
> +
> +#include <stdio.h>
> +
> +typedef int (*fptr) (int);
> +int
> +one (int a);
> +
> +int
> +two (int a);
> +
> +fptr table[] = {&one, &two};
> +
> +int
> +main()
> +{
> +  int i, x;
> +  fptr p = &one;
> +
> +  x = one (3);
> +
> +  for (i = 0; i < 350000000; i++)
> +    {
> +      x = (*p) (3);
> +      p = table[x];
> +    }
> +  printf ("done:%d\n", x);
> +}
> +
> +/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* one transformation on insn" "profile_estimate" } } */
> +/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* two transformation on insn" "profile_estimate" } } */
> +
> diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
> new file mode 100644
> index 00000000000..a8c6e365fb9
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
> @@ -0,0 +1,22 @@
> +/* It seems there is no way to avoid the other source of mulitple
> +   source testcase from being compiled independently.  Just avoid
> +   error.  */
> +#ifdef DOJOB
> +int
> +one (int a)
> +{
> +  return 1;
> +}
> +
> +int
> +two (int a)
> +{
> +  return 0;
> +}
> +#else
> +int
> +main()
> +{
> +  return 0;
> +}
> +#endif
> diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
> new file mode 100644
> index 00000000000..aa3887fde83
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
> @@ -0,0 +1,42 @@
> +/* { dg-require-effective-target lto } */
> +/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
> +/* { dg-require-profiling "-fprofile-generate" } */
> +/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate --param indir-call-topn-profile=1" } */
> +
> +#include <stdio.h>
> +
> +typedef int (*fptr) (int);
> +int
> +one (int a);
> +
> +int
> +two (int a);
> +
> +fptr table[] = {&one, &two};
> +
> +int foo ()
> +{
> +  int i, x;
> +  fptr p = &one;
> +
> +  x = one (3);
> +
> +  for (i = 0; i < 350000000; i++)
> +    {
> +      x = (*p) (3);
> +      p = table[x];
> +    }
> +  return x;
> +}
> +
> +int
> +main()
> +{
> +  int x = foo ();
> +  printf ("done:%d\n", x);
> +}
> +
> +/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* one transformation on insn" "profile_estimate" } } */
> +/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* two transformation on insn" "profile_estimate" } } */
> +
> +
> diff --git a/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
> new file mode 100644
> index 00000000000..951bc7ddd19
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
> @@ -0,0 +1,38 @@
> +/* { dg-require-profiling "-fprofile-generate" } */
> +/* { dg-options "-O2 -fdump-ipa-profile --param indir-call-topn-profile=1" } */
> +
> +#include <stdio.h>
> +
> +typedef int (*fptr) (int);
> +int
> +one (int a)
> +{
> +  return 1;
> +}
> +
> +int
> +two (int a)
> +{
> +  return 0;
> +}
> +
> +fptr table[] = {&one, &two};
> +
> +int
> +main()
> +{
> +  int i, x;
> +  fptr p = &one;
> +
> +  one (3);
> +
> +  for (i = 0; i < 350000000; i++)
> +    {
> +      x = (*p) (3);
> +      p = table[x];
> +    }
> +  printf ("done:%d\n", x);
> +}
> +
> +/* { dg-final-use-not-autofdo { scan-ipa-dump "Indirect call -> direct call.* one transformation on insn" "profile" } } */
> +/* { dg-final-use-not-autofdo { scan-ipa-dump "Indirect call -> direct call.* two transformation on insn" "profile" } } */
> diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
> index b9c1a3b1456..fe3e172fbd1 100644
> --- a/gcc/tree-inline.c
> +++ b/gcc/tree-inline.c
> @@ -2167,6 +2167,25 @@ copy_bb (copy_body_data *id, basic_block bb,
>  
>  			  gcc_assert (!edge->indirect_unknown_callee);
>  			  old_edge->speculative_call_info (direct, indirect, ref);
> +			  while (old_edge->next_callee
> +				 && old_edge->next_callee->speculative
> +				 && indirect->has_multiple_indirect_call_p ())
> +			    {
> +			      /* Some speculative calls may contain more than
> +				 one direct target, loop iterate it to clone all
> +				 related direct edges before cloning the related
> +				 indirect edge.  */
> +			      id->dst_node->clone_reference (ref, stmt);
> +
> +			      edge = old_edge->next_callee;
> +			      edge = edge->clone (id->dst_node, call_stmt,
> +						  gimple_uid (stmt), num, den,
> +						  true);
> +			      old_edge = old_edge->next_callee;
> +			      gcc_assert (!edge->indirect_unknown_callee);
> +			      old_edge->speculative_call_info (direct, indirect,
> +							       ref);
> +			    }
>  
>  			  profile_count indir_cnt = indirect->count;
>  			  indirect = indirect->clone (id->dst_node, call_stmt,
> diff --git a/gcc/tree-profile.c b/gcc/tree-profile.c
> index 4c1ead5781f..ef7748668f8 100644
> --- a/gcc/tree-profile.c
> +++ b/gcc/tree-profile.c
> @@ -74,8 +74,8 @@ static GTY(()) tree ic_tuple_callee_field;
>  /* Do initialization work for the edge profiler.  */
>  
>  /* Add code:
> -   __thread gcov*	__gcov_indirect_call_counters; // pointer to actual counter
> -   __thread void*	__gcov_indirect_call_callee; // actual callee address
> +   __thread gcov*	__gcov_indirect_call.counters; // pointer to actual counter
> +   __thread void*	__gcov_indirect_call.callee; // actual callee address
>     __thread int __gcov_function_counter; // time profiler function counter
>  */
>  static void
> @@ -382,7 +382,7 @@ gimple_gen_ic_profiler (histogram_value value, unsigned tag)
>        f_1 = foo;
>        __gcov_indirect_call.counters = &__gcov4.main[0];
>        PROF_9 = f_1;
> -      __gcov_indirect_call_callee = PROF_9;
> +      __gcov_indirect_call.callee = PROF_9;
>        _4 = f_1 ();
>     */
>  
> @@ -445,11 +445,11 @@ gimple_gen_ic_func_profiler (void)
>  
>    /* Insert code:
>  
> -     if (__gcov_indirect_call_callee != NULL)
> +     if (__gcov_indirect_call.callee != NULL)
>         __gcov_indirect_call_profiler_v3 (profile_id, &current_function_decl);
>  
>       The function __gcov_indirect_call_profiler_v3 is responsible for
> -     resetting __gcov_indirect_call_callee to NULL.  */
> +     resetting __gcov_indirect_call.callee to NULL.  */
>  
>    gimple_stmt_iterator gsi = gsi_start_bb (cond_bb);
>    void0 = build_int_cst (ptr_type_node, 0);
> @@ -891,7 +891,7 @@ pass_ipa_tree_profile::gate (function *)
>  {
>    /* When profile instrumentation, use or test coverage shall be performed.
>       But for AutoFDO, this there is no instrumentation, thus this pass is
> -     diabled.  */
> +     disabled.  */
>    return (!in_lto_p && !flag_auto_profile
>  	  && (flag_branch_probabilities || flag_test_coverage
>  	      || profile_arc_flag));
> diff --git a/gcc/value-prof.c b/gcc/value-prof.c
> index 55ea0973a03..0588df0fce9 100644
> --- a/gcc/value-prof.c
> +++ b/gcc/value-prof.c
> @@ -1406,11 +1406,10 @@ gimple_ic (gcall *icall_stmt, struct cgraph_node *direct_call,
>    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:
> - */
> +/* There maybe multiple indirect targets in histogram.  Check every
> +   indirect/virtual call if callee function exists, if not exist, leave it to
> +   LTO stage for later process.  Modify code of this indirect call to an if-else
> +   structure in ipa-profile finally.  */
>  
>  static bool
>  gimple_ic_transform (gimple_stmt_iterator *gsi)
> @@ -1434,48 +1433,57 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
>    if (!histogram)
>      return false;

The function is not correct, note that the function can now return true
when this transformation happens:
		  "Indirect call -> direct call from other "
		  "module %T=> %i (will resolve only with LTO)\n",

Current trunk returns false in that case.

>  
> -  if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
> -				  &count, &all))
> -    return false;
> +  count = 0;
> +  all = histogram->hvalue.counters[0];
>  
> -  if (4 * count <= 3 * all)
> -    return false;
> +  for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
> +    {
> +      if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
> +				      &count, &all, j))
> +	continue;

You should break here as get_nth_most_common_value (..., j + 1) will also return
false.

>  
> -  direct_call = find_func_by_profile_id ((int)val);
> +      /* Minimum probability.  should be higher than 25%.  */
> +      if (4 * count <= all)
> +	continue;

You can break here as well.

Thank you,
Martin

>  
> -  if (direct_call == NULL)
> -    {
> -      if (val)
> +      direct_call = find_func_by_profile_id ((int) val);
> +
> +      if (direct_call == NULL)
> +	{
> +	  if (val)
> +	    {
> +	      if (dump_enabled_p ())
> +		dump_printf_loc (
> +		  MSG_MISSED_OPTIMIZATION, stmt,
> +		  "Indirect call -> direct call from other "
> +		  "module %T=> %i (will resolve only with LTO)\n",
> +		  gimple_call_fn (stmt), (int) val);
> +	    }
> +	  continue;
> +	}
> +
> +      if (!check_ic_target (stmt, direct_call))
>  	{
>  	  if (dump_enabled_p ())
> -	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
> -			     "Indirect call -> direct call from other "
> -			     "module %T=> %i (will resolve only with LTO)\n",
> -			     gimple_call_fn (stmt), (int)val);
> +	    dump_printf_loc (
> +	      MSG_MISSED_OPTIMIZATION, stmt,
> +	      "Indirect call -> direct call %T => %T "
> +	      "transformation skipped because of type mismatch: %G",
> +	      gimple_call_fn (stmt), direct_call->decl, stmt);
> +	  gimple_remove_histogram_value (cfun, stmt, histogram);
> +	  return false;
>  	}
> -      return false;
> -    }
>  
> -  if (!check_ic_target (stmt, direct_call))
> -    {
>        if (dump_enabled_p ())
> -	dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
> -			 "Indirect call -> direct call %T => %T "
> -			 "transformation skipped because of type mismatch: %G",
> -			 gimple_call_fn (stmt), direct_call->decl, stmt);
> -      gimple_remove_histogram_value (cfun, stmt, histogram);
> -      return false;
> -    }
> -
> -  if (dump_enabled_p ())
> -    {
> -      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
> -		       "Indirect call -> direct call "
> -		       "%T => %T transformation on insn postponed\n",
> -		       gimple_call_fn (stmt), direct_call->decl);
> -      dump_printf_loc (MSG_NOTE, stmt,
> -		       "hist->count %" PRId64
> -		       " hist->all %" PRId64"\n", count, all);
> +	{
> +	  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
> +			   "Indirect call -> direct call "
> +			   "%T => %T transformation on insn postponed\n",
> +			   gimple_call_fn (stmt), direct_call->decl);
> +	  dump_printf_loc (MSG_NOTE, stmt,
> +			   "hist->count %" PRId64 " hist->all %" PRId64 "\n",
> +			   count, all);
> +	}
>      }
>  
>    return true;
> 


[-- Attachment #2: isempty.patch --]
[-- Type: text/x-patch, Size: 1210 bytes --]

diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index 401e304984a..faabf0ef4bc 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -1381,7 +1381,7 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
 	      /* As the direct targets are sorted by decrease, delete the first
 		 target when it is redirected.  */
 	      e2->indirect_info->indirect_call_targets->ordered_remove (0);
-	      if (!e2->indirect_info->indirect_call_targets->length ())
+	      if (e2->indirect_info->indirect_call_targets->is_empty ())
 		e2->speculative = false;
 	    }
 	  else
@@ -3872,7 +3872,7 @@ bool
 cgraph_edge::has_multiple_indirect_call_p (void)
 {
   return indirect_info && indirect_info->indirect_call_targets
-	 && indirect_info->indirect_call_targets->length () > 1;
+	 && !indirect_info->indirect_call_targets->is_empty ();
 }
 
 /* Return true if this edge has at least one indirect call target.  */
@@ -3880,7 +3880,7 @@ bool
 cgraph_edge::has_indirect_call_p (void)
 {
   return indirect_info && indirect_info->indirect_call_targets
-	 && indirect_info->indirect_call_targets->length ();
+	 && !indirect_info->indirect_call_targets->is_empty ();
 }
 
 /* A stashed copy of "symtab" for use by selftest::symbol_table_test.

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v4] Missed function specialization + partial devirtualization
  2019-09-25 10:57         ` Martin Liška
@ 2019-09-26  5:24           ` luoxhu
  2019-09-26  8:36             ` Martin Liška
  0 siblings, 1 reply; 38+ messages in thread
From: luoxhu @ 2019-09-26  5:24 UTC (permalink / raw)
  To: Martin Liška, gcc-patches; +Cc: hubicka, segher, wschmidt

[-- Attachment #1: Type: text/plain, Size: 47886 bytes --]

Thanks Martin,


On 2019/9/25 18:57, Martin Liška wrote:
> On 9/25/19 5:45 AM, luoxhu wrote:
>> Hi,
>>
>> Sorry for replying so late due to cauldron conference and other LTO issues
>> I was working on.
> 
> Hello.
> 
> That's fine, we still have plenty of time for patch review.
> 
> Not fixed issues which I reported in v3 (and still valid in v4):
> - please come up with indirect_target_info::indirect_target_info and use it
Sorry for miss out.


> - do you need to stream out indirect_call_targets when common_target_id == 0?

No need to stream out items with common_target_id == 0, removed the if 
condition in lto-cgraph.c.

> 
> Then I'm suggesting to use vec::is_empty (please see my patch).
OK.  But has_multiple_indirect_call_p should return different than 
has_indirect_call_p as it checks more that one targets?

gcc/cgraph.c
/* Return true if this edge has multiple indirect call targets.  */
  bool
  cgraph_edge::has_multiple_indirect_call_p (void)
  {
-  return indirect_info && indirect_info->indirect_call_targets
-        && indirect_info->indirect_call_targets->length () > 1;
+  return (indirect_info && indirect_info->indirect_call_targets
+         && indirect_info->indirect_call_targets->length () > 1);
  }

  /* Return true if this edge has at least one indirect call target.  */
  bool
  cgraph_edge::has_indirect_call_p (void)
  {
-  return indirect_info && indirect_info->indirect_call_targets
-        && indirect_info->indirect_call_targets->length ();
+  return (indirect_info && indirect_info->indirect_call_targets
+         && !indirect_info->indirect_call_targets->is_empty ());
  }

> 
> I see following failures for the tests provided:
> FAIL: gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c compilation,  -fprofile-generate -D_PROFILE_GENERATE
> FAIL: gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c compilation,  -fprofile-generate -D_PROFILE_GENERATE
> FAIL: gcc.dg/tree-prof/indir-call-prof-topn.c compilation,  -fprofile-generate -D_PROFILE_GENERATE

Sorry that I forgot to remove the deprecated build option in the 3 cases
(also updated the scan exp check):
-/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate --param 
indir-call-topn-profile=1" } */
+/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate" } */


The new patch is attached.  Thanks.


Xiong Hu

> 
> Next comments follow directly in the email body:
> 
>>
>> v4 Changes:
>>   1. Rebase to trunk.
>>   2. Remove num_of_ics and use vector's length to avoid redundancy.
>>   3. Update the code in ipa-profile.c to improve review feasibility.
>>   4. Add function has_indirect_call_p and has_multiple_indirect_call_p.
>>   5. For parameter control, I will leave it to next patch as it is a
>>      relative independent function.  Currently, maximum number of
>>      promotions is GCOV_TOPN_VALUES as only 4 profiling value limited
>>      from profile-generate, therefore minimum probability is adjusted to
>>      25% in value-prof.c, it was 75% also by hard code for single
>>      indirect target.  No control to minimal number of edge
>>      executions yet.  What's more, this patch is a bit large now.
>>
>> This patch aims to fix PR69678 caused by PGO indirect call profiling
>> performance issues.
>> The bug that profiling data is never working was fixed by Martin's pull
>> back of topN patches, performance got GEOMEAN ~1% improvement(+24% for
>> 511.povray_r specifically).
>> Still, currently the default profile only generates SINGLE indirect target
>> that called more than 75%.  This patch leverages MULTIPLE indirect
>> targets use in LTO-WPA and LTO-LTRANS stage, as a result, function
>> specialization, profiling, partial devirtualization, inlining and
>> cloning could be done successfully based on it.
>> Performance can get improved from 0.70 sec to 0.38 sec on simple tests.
>> Details are:
>>    1.  PGO with topn is enabled by default now, but only one indirect
>>    target edge will be generated in ipa-profile pass, so add variables to enable
>>    multiple speculative edges through passes, speculative_id will record the
>>    direct edge index bind to the indirect edge, indirect_call_targets length
>>    records how many direct edges owned by the indirect edge, postpone gimple_ic
>>    to ipa-profile like default as inline pass will decide whether it is benefit
>>    to transform indirect call.
>>    2.  Use speculative_id to track and search the reference node matched
>>    with the direct edge's callee for multiple targets.  Actually, it is the
>>    caller's responsibility to handle the direct edges mapped to same indirect
>>    edge.  speculative_call_info will return one of the direct edge specified,
>>    this will leverage current IPA edge process framework mostly.
>>    3.  Enable LTO WPA/LTRANS stage multiple indirect call targets analysis for
>>    profile full support in ipa passes and cgraph_edge functions.  speculative_id
>>    can be set by make_speculative id when multiple targets are binded to
>>    one indirect edge, and cloned if new edge is cloned.  speculative_id
>>    is streamed out and stream int by lto like lto_stmt_uid.
>>    4.  Add 1 in module testcase and 2 cross module testcases.
>>    5.  Bootstrap and regression test passed on Power8-LE.  No function
>>    and performance regression for SPEC2017.
>>
>> gcc/ChangeLog
>>
>> 	2019-09-25  Xiong Hu Luo  <luoxhu@linux.ibm.com>
>>
>> 	PR ipa/69678
>> 	* cgraph.c (symbol_table::create_edge): Init speculative_id.
>> 	(cgraph_edge::make_speculative): Add param for setting speculative_id.
>> 	(cgraph_edge::speculative_call_info): Find reference by
>> 	speculative_id for multiple indirect targets.
>> 	(cgraph_edge::resolve_speculation): Decrease the speculations
>> 	for indirect edge, drop it's speculative if not direct target
>> 	left.
>> 	(cgraph_edge::redirect_call_stmt_to_callee): Likewise.
>> 	(cgraph_node::verify_node): Don't report error if speculative
>> 	edge not include statement.
>> 	(cgraph_edge::has_multiple_indirect_call_p): New function.
>> 	(cgraph_edge::has_indirect_call_p): New function.
>> 	* cgraph.h (struct indirect_target_info): New struct.
>> 	(indirect_call_targets): New vector variable.
>> 	(make_speculative): Add param for setting speculative_id.
>> 	(cgraph_edge::has_multiple_indirect_call_p): New declare.
>> 	(cgraph_edge::has_indirect_call_p): New declare.
>> 	(speculative_id): New variable.
>> 	* cgraphclones.c (cgraph_node::create_clone): Clone speculative_id.
>> 	* ipa-inline.c (inline_small_functions): Fix iterator update.
>> 	* ipa-profile.c (ipa_profile_generate_summary): Add indirect
>> 	multiple targets logic.
>> 	(ipa_profile): Likewise.
>> 	* ipa-ref.h (speculative_id): New variable.
>> 	* ipa.c (process_references): Fix typo.
>> 	* lto-cgraph.c (lto_output_edge): Add indirect multiple targets
>> 	logic.  Stream out speculative_id.
>> 	(input_edge): Likewise.
>> 	* predict.c (dump_prediction): Remove edges count assert to be
>> 	precise.
>> 	* symtab.c (symtab_node::create_reference): Init speculative_id.
>> 	(symtab_node::clone_references): Clone speculative_id.
>> 	(symtab_node::clone_referring): Clone speculative_id.
>> 	(symtab_node::clone_reference): Clone speculative_id.
>> 	(symtab_node::clear_stmts_in_references): Clear speculative_id.
>> 	* tree-inline.c (copy_bb): Duplicate all the speculative edges
>> 	if indirect call contains multiple speculative targets.
>> 	* tree-profile.c (gimple_gen_ic_profiler): Use the new variable
>> 	__gcov_indirect_call.counters and __gcov_indirect_call.callee.
>> 	(gimple_gen_ic_func_profiler): Likewise.
>> 	(pass_ipa_tree_profile::gate): Fix comment typos.
>> 	* value-prof.c  (gimple_ic_transform): Handle topn case.
>> 	Fix comment typos.
>>
>> gcc/testsuite/ChangeLog
>>
>> 	2019-09-25  Xiong Hu Luo  <luoxhu@linux.ibm.com>
>>
>> 	PR ipa/69678
>> 	* gcc.dg/tree-prof/indir-call-prof-topn.c: New testcase.
>> 	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c: New testcase.
>> 	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c: New testcase.
>> 	* gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c: New testcase.
>> ---
>>   gcc/cgraph.c                                  | 90 ++++++++++++++++-
>>   gcc/cgraph.h                                  | 29 +++++-
>>   gcc/cgraphclones.c                            |  1 +
>>   gcc/ipa-inline.c                              | 15 +--
>>   gcc/ipa-profile.c                             | 96 ++++++++++++++-----
>>   gcc/ipa-ref.h                                 |  1 +
>>   gcc/ipa.c                                     |  2 +-
>>   gcc/lto-cgraph.c                              | 57 +++++++++--
>>   gcc/predict.c                                 |  1 -
>>   gcc/symtab.c                                  |  5 +
>>   .../tree-prof/crossmodule-indir-call-topn-1.c | 35 +++++++
>>   .../crossmodule-indir-call-topn-1a.c          | 22 +++++
>>   .../tree-prof/crossmodule-indir-call-topn-2.c | 42 ++++++++
>>   .../gcc.dg/tree-prof/indir-call-prof-topn.c   | 38 ++++++++
>>   gcc/tree-inline.c                             | 19 ++++
>>   gcc/tree-profile.c                            | 12 +--
>>   gcc/value-prof.c                              | 86 +++++++++--------
>>   17 files changed, 452 insertions(+), 99 deletions(-)
>>   create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
>>   create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
>>   create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
>>   create mode 100644 gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
>>
>> diff --git a/gcc/cgraph.c b/gcc/cgraph.c
>> index 843891e9e56..9a28aca435f 100644
>> --- a/gcc/cgraph.c
>> +++ b/gcc/cgraph.c
>> @@ -860,6 +860,7 @@ symbol_table::create_edge (cgraph_node *caller, cgraph_node *callee,
>>     edge->prev_callee = NULL;
>>     edge->next_callee = NULL;
>>     edge->lto_stmt_uid = 0;
>> +  edge->speculative_id = 0;
>>   
>>     edge->count = count;
>>   
>> @@ -1051,7 +1052,8 @@ cgraph_edge::remove (void)
>>      Return direct edge created.  */
>>   
>>   cgraph_edge *
>> -cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
>> +cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count,
>> +			       unsigned int speculative_id)
>>   {
>>     cgraph_node *n = caller;
>>     ipa_ref *ref = NULL;
>> @@ -1069,11 +1071,13 @@ cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
>>     else
>>       e2->can_throw_external = can_throw_external;
>>     e2->lto_stmt_uid = lto_stmt_uid;
>> +  e2->speculative_id = speculative_id;
>>     e2->in_polymorphic_cdtor = in_polymorphic_cdtor;
>>     count -= e2->count;
>>     symtab->call_edge_duplication_hooks (this, e2);
>>     ref = n->create_reference (n2, IPA_REF_ADDR, call_stmt);
>>     ref->lto_stmt_uid = lto_stmt_uid;
>> +  ref->speculative_id = speculative_id;
>>     ref->speculative = speculative;
>>     n2->mark_address_taken ();
>>     return e2;
>> @@ -1087,6 +1091,38 @@ cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
>>      call) and if one of them exists, all of them must exist.
>>   
>>      Given speculative call edge, return all three components.
>> +
>> +   For some indirect edge, it may maps to multiple direct edges, i.e.  1:N.
>> +   check the speculative_id to return all the three components for specified
>> +   direct edge or indirect edge.
>> +   If input is indirect, caller of this function will get the direct edge one by
>> +   one, get_edge will just return one of the direct edge mapped to the indirect
>> +   edge, the returned direct edge will be resolved or redirected by the caller,
>> +   then number of indirect calls (speculations) is deceased in each access.
>> +   If input is direct, this function will get the indirect edge and reference
>> +   with matched speculative_id, the returned edge will also be resolved or
>> +   redirected, decrease the speculations accordingly.
>> +   Speculations of indirect edge will be dropped only if all direct edges
>> +   be handled.
>> +
>> +   e.g.  for indirect edge E statement "call call_dest":
>> +
>> +   Redirect N3 after redirected N2:
>> +
>> +   if (call_dest == N2)
>> +     n2 ();
>> +   else if (call_dest == N3)
>> +     n3 ();
>> +   else
>> +     call call_dest
>> +
>> +   Resolve N3 and only redirect N2:
>> +
>> +   if (call_dest == N2)
>> +     n2 ();
>> +   else
>> +     call call_dest
>> +
>>    */
>>   
>>   void
>> @@ -1126,7 +1162,7 @@ cgraph_edge::speculative_call_info (cgraph_edge *&direct,
>>   
>>     reference = NULL;
>>     for (i = 0; e->caller->iterate_reference (i, ref); i++)
>> -    if (ref->speculative
>> +    if (ref->speculative && ref->speculative_id == e->speculative_id
>>   	&& ((ref->stmt && ref->stmt == e->call_stmt)
>>   	    || (!ref->stmt && ref->lto_stmt_uid == e->lto_stmt_uid)))
>>         {
>> @@ -1187,7 +1223,21 @@ cgraph_edge::resolve_speculation (tree callee_decl)
>>            in the functions inlined through it.  */
>>       }
>>     edge->count += e2->count;
>> -  edge->speculative = false;
>> +  /* edge is indirect, e2 is direct.  If edge contains multiple speculations,
>> +     remove one of speculations for this indirect edge, then if edge still
>> +     contains direct target, keep the speculation, next direct target
>> +     will continue use it.  Give up speculation completely if no direct
>> +     target is left for this indirect edge.  */
>> +  if (edge->has_indirect_call_p ())
>> +    {
>> +      /* As the direct targets are sorted by decrease, delete the first target
>> +	 when it is resolved.  */
>> +      edge->indirect_info->indirect_call_targets->ordered_remove (0);
>> +      if (!edge->indirect_info->indirect_call_targets->length ())
>> +	edge->speculative = false;
>> +    }
>> +  else
>> +    edge->speculative = false;
>>     e2->speculative = false;
>>     ref->remove_reference ();
>>     if (e2->indirect_unknown_callee || e2->inline_failed)
>> @@ -1321,7 +1371,21 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
>>   	  e->caller->set_call_stmt_including_clones (e->call_stmt, new_stmt,
>>   						     false);
>>   	  e->count = gimple_bb (e->call_stmt)->count;
>> -	  e2->speculative = false;
>> +	  /* edge is direct, e2 is indirect here.  If e2 contains multiple
>> +	     speculations, remove one of speculations for this indirect edge,
>> +	     then if e2 still contains direct target, keep the speculation,
>> +	     next direct target will continue use it.  Give up speculation
>> +	     completely if no direct target is left for this indirect e2.  */
>> +	  if (e2->has_indirect_call_p ())
>> +	    {
>> +	      /* As the direct targets are sorted by decrease, delete the first
>> +		 target when it is redirected.  */
>> +	      e2->indirect_info->indirect_call_targets->ordered_remove (0);
>> +	      if (!e2->indirect_info->indirect_call_targets->length ())
>> +		e2->speculative = false;
>> +	    }
>> +	  else
>> +	    e2->speculative = false;
>>   	  e2->count = gimple_bb (e2->call_stmt)->count;
>>   	  ref->speculative = false;
>>   	  ref->stmt = NULL;
>> @@ -3445,7 +3509,7 @@ cgraph_node::verify_node (void)
>>   
>>         for (e = callees; e; e = e->next_callee)
>>   	{
>> -	  if (!e->aux)
>> +	  if (!e->aux && !e->speculative)
>>   	    {
>>   	      error ("edge %s->%s has no corresponding call_stmt",
>>   		     identifier_to_locale (e->caller->name ()),
>> @@ -3872,6 +3936,22 @@ cgraph_edge::possibly_call_in_translation_unit_p (void)
>>     return node->get_availability () >= AVAIL_AVAILABLE;
>>   }
>>   
>> +/* Return true if this edge has multiple indirect call targets.  */
>> +bool
>> +cgraph_edge::has_multiple_indirect_call_p (void)
>> +{
>> +  return indirect_info && indirect_info->indirect_call_targets
>> +	 && indirect_info->indirect_call_targets->length () > 1;
>> +}
> 
> For multiline && expression, we typically wrap the whole condition
> in '(' and ')'.
> 
>> +
>> +/* Return true if this edge has at least one indirect call target.  */
>> +bool
>> +cgraph_edge::has_indirect_call_p (void)
>> +{
>> +  return indirect_info && indirect_info->indirect_call_targets
>> +	 && indirect_info->indirect_call_targets->length ();
>> +}
> 
> Likewise here.
> 
>> +
>>   /* A stashed copy of "symtab" for use by selftest::symbol_table_test.
>>      This needs to be a global so that it can be a GC root, and thus
>>      prevent the stashed copy from being garbage-collected if the GC runs
>> diff --git a/gcc/cgraph.h b/gcc/cgraph.h
>> index 4c54210123a..33c8454c4e0 100644
>> --- a/gcc/cgraph.h
>> +++ b/gcc/cgraph.h
>> @@ -1636,6 +1636,16 @@ private:
>>     void make_speculative (tree otr_type = NULL);
>>   };
>>   
>> +/* Structure containing indirect target information from profile.  */
>> +
>> +struct GTY (()) indirect_target_info
>> +{
>> +  /* Profile_id of common target obtained from profile.  */
>> +  unsigned int common_target_id;
>> +  /* Probability that call will land in function with COMMON_TARGET_ID.  */
>> +  int common_target_probability;
>> +};
>> +
>>   /* Structure containing additional information about an indirect call.  */
>>   
>>   class GTY(()) cgraph_indirect_call_info
>> @@ -1654,10 +1664,9 @@ public:
>>     int param_index;
>>     /* ECF flags determined from the caller.  */
>>     int ecf_flags;
>> -  /* Profile_id of common target obtrained from profile.  */
>> -  int common_target_id;
>> -  /* Probability that call will land in function with COMMON_TARGET_ID.  */
>> -  int common_target_probability;
>> +
>> +  /* An indirect call may contain one or multiple call targets.  */
>> +  vec<indirect_target_info, va_gc> *indirect_call_targets;
>>   
>>     /* Set when the call is a virtual call with the parameter being the
>>        associated object pointer rather than a simple direct call.  */
>> @@ -1714,7 +1723,8 @@ public:
>>     /* Turn edge into speculative call calling N2. Update
>>        the profile so the direct call is taken COUNT times
>>        with FREQUENCY.  */
>> -  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count);
>> +  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count,
>> +				 unsigned int speculative_id = 0);
>>   
>>      /* Given speculative call edge, return all three components.  */
>>     void speculative_call_info (cgraph_edge *&direct, cgraph_edge *&indirect,
>> @@ -1773,6 +1783,12 @@ public:
>>        be internal to the current translation unit.  */
>>     bool possibly_call_in_translation_unit_p (void);
>>   
>> +  /* Return true if this edge has multiple indirect call targets.  */
>> +  bool has_multiple_indirect_call_p (void);
>> +
>> +  /* Return true if this edge has at least one indirect call target.  */
>> +  bool has_indirect_call_p (void);
>> +
>>     /* Expected number of executions: calculated in profile.c.  */
>>     profile_count count;
>>     cgraph_node *caller;
>> @@ -1792,6 +1808,9 @@ public:
>>     /* The stmt_uid of call_stmt.  This is used by LTO to recover the call_stmt
>>        when the function is serialized in.  */
>>     unsigned int lto_stmt_uid;
>> +  /* speculative id is used by multiple indirect targets when the function is
>> +   speculated.  */
>> +  unsigned int speculative_id;
>>     /* Whether this edge was made direct by indirect inlining.  */
>>     unsigned int indirect_inlining_edge : 1;
>>     /* Whether this edge describes an indirect call with an undetermined
>> diff --git a/gcc/cgraphclones.c b/gcc/cgraphclones.c
>> index fa753697c78..5dbd8d90b77 100644
>> --- a/gcc/cgraphclones.c
>> +++ b/gcc/cgraphclones.c
>> @@ -128,6 +128,7 @@ cgraph_edge::clone (cgraph_node *n, gcall *call_stmt, unsigned stmt_uid,
>>     new_edge->inline_failed = inline_failed;
>>     new_edge->indirect_inlining_edge = indirect_inlining_edge;
>>     new_edge->lto_stmt_uid = stmt_uid;
>> +  new_edge->speculative_id = speculative_id;
>>     /* Clone flags that depend on call_stmt availability manually.  */
>>     new_edge->can_throw_external = can_throw_external;
>>     new_edge->call_stmt_cannot_inline_p = call_stmt_cannot_inline_p;
>> diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
>> index b62d280eb25..6136214f9ac 100644
>> --- a/gcc/ipa-inline.c
>> +++ b/gcc/ipa-inline.c
>> @@ -1881,12 +1881,15 @@ inline_small_functions (void)
>>   	}
>>         if (has_speculative)
>>   	for (edge = node->callees; edge; edge = next)
>> -	  if (edge->speculative && !speculation_useful_p (edge,
>> -							  edge->aux != NULL))
>> -	    {
>> -	      edge->resolve_speculation ();
>> -	      update = true;
>> -	    }
>> +	  {
>> +	    next = edge->next_callee;
>> +	    if (edge->speculative
>> +		&& !speculation_useful_p (edge, edge->aux != NULL))
>> +	      {
>> +		edge->resolve_speculation ();
>> +		update = true;
>> +	      }
>> +	  }
>>         if (update)
>>   	{
>>   	  struct cgraph_node *where = node->global.inlined_to
>> diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
>> index 970dba39c80..342e8ea05d1 100644
>> --- a/gcc/ipa-profile.c
>> +++ b/gcc/ipa-profile.c
>> @@ -192,23 +192,35 @@ ipa_profile_generate_summary (void)
>>   		  if (h)
>>   		    {
>>   		      gcov_type val, count, all;
>> -		      if (get_nth_most_common_value (NULL, "indirect call", h,
>> -						     &val, &count, &all))
>> +		      struct cgraph_edge *e = node->get_edge (stmt);
>> +		      if (e && !e->indirect_unknown_callee)
>> +			continue;
>> +
>> +		      struct indirect_target_info item;
>> +		      for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
>>   			{
>> -			  struct cgraph_edge * e = node->get_edge (stmt);
>> -			  if (e && !e->indirect_unknown_callee)
>> +			  if (!get_nth_most_common_value (NULL, "indirect call",
>> +							  h, &val, &count, &all,
>> +							  j))
>> +			    continue;
>> +
>> +			  if (val == 0)
>>   			    continue;
>>   
>> -			  e->indirect_info->common_target_id = val;
>> -			  e->indirect_info->common_target_probability
>> +			  item.common_target_id = val;
>> +			  item.common_target_probability
>>   			    = GCOV_COMPUTE_SCALE (count, all);
> 
> There's one of the places where you can use the constructor.
> 
>> -			  if (e->indirect_info->common_target_probability > REG_BR_PROB_BASE)
>> +			  if (item.common_target_probability > REG_BR_PROB_BASE)
>>   			    {
>>   			      if (dump_file)
>> -				fprintf (dump_file, "Probability capped to 1\n");
>> -			      e->indirect_info->common_target_probability = REG_BR_PROB_BASE;
>> +				fprintf (dump_file,
>> +					 "Probability capped to 1\n");
>> +			      item.common_target_probability = REG_BR_PROB_BASE;
>>   			    }
>> +			  vec_safe_push (
>> +			    e->indirect_info->indirect_call_targets, item);
>>   			}
>> +
>>   		      gimple_remove_histogram_value (DECL_STRUCT_FUNCTION (node->decl),
>>   						      stmt, h);
>>   		    }
>> @@ -492,6 +504,7 @@ ipa_profile (void)
>>     int nindirect = 0, ncommon = 0, nunknown = 0, nuseless = 0, nconverted = 0;
>>     int nmismatch = 0, nimpossible = 0;
>>     bool node_map_initialized = false;
>> +  gcov_type threshold;
>>   
>>     if (dump_file)
>>       dump_histogram (dump_file, histogram);
>> @@ -500,14 +513,12 @@ ipa_profile (void)
>>         overall_time += histogram[i]->count * histogram[i]->time;
>>         overall_size += histogram[i]->size;
>>       }
>> +  threshold = 0;
>>     if (overall_time)
>>       {
>> -      gcov_type threshold;
>> -
>>         gcc_assert (overall_size);
>>   
>>         cutoff = (overall_time * PARAM_VALUE (HOT_BB_COUNT_WS_PERMILLE) + 500) / 1000;
>> -      threshold = 0;
>>         for (i = 0; cumulated < cutoff; i++)
>>   	{
>>   	  cumulated += histogram[i]->count * histogram[i]->time;
>> @@ -543,7 +554,7 @@ ipa_profile (void)
>>     histogram.release ();
>>     histogram_pool.release ();
>>   
>> -  /* Produce speculative calls: we saved common traget from porfiling into
>> +  /* Produce speculative calls: we saved common target from profiling into
>>        e->common_target_id.  Now, at link time, we can look up corresponding
>>        function node and produce speculative call.  */
>>   
>> @@ -558,13 +569,37 @@ ipa_profile (void)
>>   	{
>>   	  if (n->count.initialized_p ())
>>   	    nindirect++;
>> -	  if (e->indirect_info->common_target_id)
>> +	  if (e->has_indirect_call_p ())
>>   	    {
>>   	      if (!node_map_initialized)
>> -	        init_node_map (false);
>> +		init_node_map (false);
>>   	      node_map_initialized = true;
>>   	      ncommon++;
>> -	      n2 = find_func_by_profile_id (e->indirect_info->common_target_id);
>> +
>> +	      if (in_lto_p)
>> +		{
>> +		  if (dump_file)
>> +		    {
>> +		      fprintf (dump_file,
>> +			       "Updating hotness threshold in LTO mode.\n");
>> +		      fprintf (dump_file, "Updated min count: %" PRId64 "\n",
>> +			       (int64_t) threshold);
>> +		    }
>> +		  set_hot_bb_threshold (threshold
>> +		    / e->indirect_info->indirect_call_targets->length ());
>> +		}
>> +
>> +	      unsigned speculative_id = 0;
>> +	      struct indirect_target_info *item;
>> +	      /* The code below is not formatted yet for review convenience.
>> +		 Move to a seprate small function is not easy as too many local
>> +		 variables used in it.  Need format and remove this comments
>> +		 once got approved.  */
>> +	      FOR_EACH_VEC_SAFE_ELT (e->indirect_info->indirect_call_targets, i,
>> +				     item)
>> +	       {
>> +	      bool speculative_found = false;
>> +	      n2 = find_func_by_profile_id (item->common_target_id);
>>   	      if (n2)
>>   		{
>>   		  if (dump_file)
>> @@ -573,11 +608,10 @@ ipa_profile (void)
>>   			       " other module %s => %s, prob %3.2f\n",
>>   			       n->dump_name (),
>>   			       n2->dump_name (),
>> -			       e->indirect_info->common_target_probability
>> -			       / (float)REG_BR_PROB_BASE);
>> +			       item->common_target_probability
>> +				 / (float) REG_BR_PROB_BASE);
>>   		    }
>> -		  if (e->indirect_info->common_target_probability
>> -		      < REG_BR_PROB_BASE / 2)
>> +		  if (item->common_target_probability < REG_BR_PROB_BASE / 2)
>>   		    {
>>   		      nuseless++;
>>   		      if (dump_file)
>> @@ -613,7 +647,7 @@ ipa_profile (void)
>>   		      if (dump_file)
>>   			fprintf (dump_file,
>>   				 "Not speculating: "
>> -				 "parameter count mistmatch\n");
>> +				 "parameter count mismatch\n");
>>   		    }
>>   		  else if (e->indirect_info->polymorphic
>>   			   && !opt_for_fn (n->decl, flag_devirtualize)
>> @@ -640,20 +674,30 @@ ipa_profile (void)
>>   			    n2 = alias;
>>   			}
>>   		      nconverted++;
>> -		      e->make_speculative
>> -			(n2,
>> -			 e->count.apply_probability
>> -				     (e->indirect_info->common_target_probability));
>> +		      e->make_speculative (n2,
>> +					   e->count.apply_probability (
>> +					     item->common_target_probability),
>> +					   speculative_id);
>>   		      update = true;
>> +		      speculative_id++;
>> +		      speculative_found = true;
>>   		    }
>>   		}
>>   	      else
>>   		{
>>   		  if (dump_file)
>>   		    fprintf (dump_file, "Function with profile-id %i not found.\n",
>> -			     e->indirect_info->common_target_id);
>> +			     item->common_target_id);
>>   		  nunknown++;
>>   		}
>> +	      if (!speculative_found)
>> +		{
>> +		  /* Remove item from indirect_call_targets if no
>> +		     speculative edge generated, rollback the iteration.  */
>> +		  e->indirect_info->indirect_call_targets->ordered_remove (i);
>> +		  i--;
>> +		}
>> +	       }
>>   	    }
>>   	 }
>>          if (update)
>> diff --git a/gcc/ipa-ref.h b/gcc/ipa-ref.h
>> index 0d8e509c932..3e6562ec9d1 100644
>> --- a/gcc/ipa-ref.h
>> +++ b/gcc/ipa-ref.h
>> @@ -59,6 +59,7 @@ public:
>>     symtab_node *referred;
>>     gimple *stmt;
>>     unsigned int lto_stmt_uid;
>> +  unsigned int speculative_id;
>>     unsigned int referred_index;
>>     ENUM_BITFIELD (ipa_ref_use) use:3;
>>     unsigned int speculative:1;
>> diff --git a/gcc/ipa.c b/gcc/ipa.c
>> index 6b84e1f9bda..a10b0603f14 100644
>> --- a/gcc/ipa.c
>> +++ b/gcc/ipa.c
>> @@ -166,7 +166,7 @@ process_references (symtab_node *snode,
>>      devirtualization happens.  After inlining still keep their declarations
>>      around, so we can devirtualize to a direct call.
>>   
>> -   Also try to make trivial devirutalization when no or only one target is
>> +   Also try to make trivial devirtualization when no or only one target is
>>      possible.  */
>>   
>>   static void
>> diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
>> index bc0f0107333..61380dcc7b8 100644
>> --- a/gcc/lto-cgraph.c
>> +++ b/gcc/lto-cgraph.c
>> @@ -238,6 +238,7 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
>>     unsigned int uid;
>>     intptr_t ref;
>>     struct bitpack_d bp;
>> +  unsigned len;
>>   
>>     if (edge->indirect_unknown_callee)
>>       streamer_write_enum (ob->main_stream, LTO_symtab_tags, LTO_symtab_last_tag,
>> @@ -265,6 +266,7 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
>>     bp_pack_enum (&bp, cgraph_inline_failed_t,
>>   	        CIF_N_REASONS, edge->inline_failed);
>>     bp_pack_var_len_unsigned (&bp, uid);
>> +  bp_pack_var_len_unsigned (&bp, edge->speculative_id);
>>     bp_pack_value (&bp, edge->indirect_inlining_edge, 1);
>>     bp_pack_value (&bp, edge->speculative, 1);
>>     bp_pack_value (&bp, edge->call_stmt_cannot_inline_p, 1);
>> @@ -291,11 +293,27 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
>>     streamer_write_bitpack (&bp);
>>     if (edge->indirect_unknown_callee)
>>       {
>> -      streamer_write_hwi_stream (ob->main_stream,
>> -			         edge->indirect_info->common_target_id);
>> -      if (edge->indirect_info->common_target_id)
>> -	streamer_write_hwi_stream
>> -	   (ob->main_stream, edge->indirect_info->common_target_probability);
>> +      struct indirect_target_info *item;
>> +      unsigned int i;
>> +      len = edge->has_indirect_call_p ()
>> +	      ? edge->indirect_info->indirect_call_targets->length ()
>> +	      : 0;
>> +      gcc_assert (len <= GCOV_TOPN_VALUES);
>> +
>> +      streamer_write_hwi_stream (ob->main_stream, len);
>> +
>> +      if (len)
>> +	{
>> +	  FOR_EACH_VEC_SAFE_ELT (edge->indirect_info->indirect_call_targets, i,
>> +				 item)
>> +	    {
>> +	      streamer_write_hwi_stream (ob->main_stream,
>> +					 item->common_target_id);
>> +	      if (item->common_target_id)
>> +		streamer_write_hwi_stream (ob->main_stream,
>> +					   item->common_target_probability);
>> +	    }
>> +	}
>>       }
>>   }
>>   
>> @@ -688,6 +706,7 @@ lto_output_ref (struct lto_simple_output_block *ob, struct ipa_ref *ref,
>>         if (ref->stmt)
>>   	uid = gimple_uid (ref->stmt) + 1;
>>         streamer_write_hwi_stream (ob->main_stream, uid);
>> +      streamer_write_hwi_stream (ob->main_stream, ref->speculative_id);
>>       }
>>   }
>>   
>> @@ -1419,7 +1438,10 @@ input_ref (class lto_input_block *ib,
>>     ref = referring_node->create_reference (node, use);
>>     ref->speculative = speculative;
>>     if (is_a <cgraph_node *> (referring_node))
>> -    ref->lto_stmt_uid = streamer_read_hwi (ib);
>> +    {
>> +      ref->lto_stmt_uid = streamer_read_hwi (ib);
>> +      ref->speculative_id = streamer_read_hwi (ib);
>> +    }
>>   }
>>   
>>   /* Read an edge from IB.  NODES points to a vector of previously read nodes for
>> @@ -1433,11 +1455,12 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
>>   {
>>     struct cgraph_node *caller, *callee;
>>     struct cgraph_edge *edge;
>> -  unsigned int stmt_id;
>> +  unsigned int stmt_id, speculative_id;
>>     profile_count count;
>>     cgraph_inline_failed_t inline_failed;
>>     struct bitpack_d bp;
>>     int ecf_flags = 0;
>> +  unsigned i, len;
>>   
>>     caller = dyn_cast<cgraph_node *> (nodes[streamer_read_hwi (ib)]);
>>     if (caller == NULL || caller->decl == NULL_TREE)
>> @@ -1457,6 +1480,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
>>     bp = streamer_read_bitpack (ib);
>>     inline_failed = bp_unpack_enum (&bp, cgraph_inline_failed_t, CIF_N_REASONS);
>>     stmt_id = bp_unpack_var_len_unsigned (&bp);
>> +  speculative_id = bp_unpack_var_len_unsigned (&bp);
>>   
>>     if (indirect)
>>       edge = caller->create_indirect_edge (NULL, 0, count);
>> @@ -1466,6 +1490,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
>>     edge->indirect_inlining_edge = bp_unpack_value (&bp, 1);
>>     edge->speculative = bp_unpack_value (&bp, 1);
>>     edge->lto_stmt_uid = stmt_id;
>> +  edge->speculative_id = speculative_id;
>>     edge->inline_failed = inline_failed;
>>     edge->call_stmt_cannot_inline_p = bp_unpack_value (&bp, 1);
>>     edge->can_throw_external = bp_unpack_value (&bp, 1);
>> @@ -1485,9 +1510,21 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
>>         if (bp_unpack_value (&bp, 1))
>>   	ecf_flags |= ECF_RETURNS_TWICE;
>>         edge->indirect_info->ecf_flags = ecf_flags;
>> -      edge->indirect_info->common_target_id = streamer_read_hwi (ib);
>> -      if (edge->indirect_info->common_target_id)
>> -        edge->indirect_info->common_target_probability = streamer_read_hwi (ib);
>> +
>> +      len = streamer_read_hwi (ib);
>> +
>> +      gcc_assert (len <= GCOV_TOPN_VALUES);
>> +
>> +      if (len)
>> +	{
>> +	  indirect_target_info item;
>> +	  for (i = 0; i < len; i++)
>> +	    {
>> +	      item.common_target_id = streamer_read_hwi (ib);
>> +	      item.common_target_probability = streamer_read_hwi (ib);
>> +	      vec_safe_push (edge->indirect_info->indirect_call_targets, item);
>> +	    }
>> +	}
>>       }
>>   }
>>   
>> diff --git a/gcc/predict.c b/gcc/predict.c
>> index 915f0806b11..3f56fa3a74a 100644
>> --- a/gcc/predict.c
>> +++ b/gcc/predict.c
>> @@ -762,7 +762,6 @@ dump_prediction (FILE *file, enum br_predictor predictor, int probability,
>>         && bb->count.precise_p ()
>>         && reason == REASON_NONE)
>>       {
>> -      gcc_assert (e->count ().precise_p ());
>>         fprintf (file, ";;heuristics;%s;%" PRId64 ";%" PRId64 ";%.1f;\n",
>>   	       predictor_info[predictor].name,
>>   	       bb->count.to_gcov_type (), e->count ().to_gcov_type (),
>> diff --git a/gcc/symtab.c b/gcc/symtab.c
>> index ee9723c3453..d4c36fd3e5a 100644
>> --- a/gcc/symtab.c
>> +++ b/gcc/symtab.c
>> @@ -603,6 +603,7 @@ symtab_node::create_reference (symtab_node *referred_node,
>>     ref->referred = referred_node;
>>     ref->stmt = stmt;
>>     ref->lto_stmt_uid = 0;
>> +  ref->speculative_id = 0;
>>     ref->use = use_type;
>>     ref->speculative = 0;
>>   
>> @@ -660,6 +661,7 @@ symtab_node::clone_references (symtab_node *node)
>>         ref2 = create_reference (ref->referred, ref->use, ref->stmt);
>>         ref2->speculative = speculative;
>>         ref2->lto_stmt_uid = stmt_uid;
>> +      ref2->speculative_id = ref->speculative_id;
>>       }
>>   }
>>   
>> @@ -678,6 +680,7 @@ symtab_node::clone_referring (symtab_node *node)
>>         ref2 = ref->referring->create_reference (this, ref->use, ref->stmt);
>>         ref2->speculative = speculative;
>>         ref2->lto_stmt_uid = stmt_uid;
>> +      ref2->speculative_id = ref->speculative_id;
>>       }
>>   }
>>   
>> @@ -693,6 +696,7 @@ symtab_node::clone_reference (ipa_ref *ref, gimple *stmt)
>>     ref2 = create_reference (ref->referred, ref->use, stmt);
>>     ref2->speculative = speculative;
>>     ref2->lto_stmt_uid = stmt_uid;
>> +  ref2->speculative_id = ref->speculative_id;
>>     return ref2;
>>   }
>>   
>> @@ -747,6 +751,7 @@ symtab_node::clear_stmts_in_references (void)
>>         {
>>   	r->stmt = NULL;
>>   	r->lto_stmt_uid = 0;
>> +	r->speculative_id = 0;
>>         }
>>   }
>>   
>> diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
>> new file mode 100644
>> index 00000000000..e0a83c2e067
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
>> @@ -0,0 +1,35 @@
>> +/* { dg-require-effective-target lto } */
>> +/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
>> +/* { dg-require-profiling "-fprofile-generate" } */
>> +/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate --param indir-call-topn-profile=1" } */
>> +
>> +#include <stdio.h>
>> +
>> +typedef int (*fptr) (int);
>> +int
>> +one (int a);
>> +
>> +int
>> +two (int a);
>> +
>> +fptr table[] = {&one, &two};
>> +
>> +int
>> +main()
>> +{
>> +  int i, x;
>> +  fptr p = &one;
>> +
>> +  x = one (3);
>> +
>> +  for (i = 0; i < 350000000; i++)
>> +    {
>> +      x = (*p) (3);
>> +      p = table[x];
>> +    }
>> +  printf ("done:%d\n", x);
>> +}
>> +
>> +/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* one transformation on insn" "profile_estimate" } } */
>> +/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* two transformation on insn" "profile_estimate" } } */
>> +
>> diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
>> new file mode 100644
>> index 00000000000..a8c6e365fb9
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
>> @@ -0,0 +1,22 @@
>> +/* It seems there is no way to avoid the other source of mulitple
>> +   source testcase from being compiled independently.  Just avoid
>> +   error.  */
>> +#ifdef DOJOB
>> +int
>> +one (int a)
>> +{
>> +  return 1;
>> +}
>> +
>> +int
>> +two (int a)
>> +{
>> +  return 0;
>> +}
>> +#else
>> +int
>> +main()
>> +{
>> +  return 0;
>> +}
>> +#endif
>> diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
>> new file mode 100644
>> index 00000000000..aa3887fde83
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
>> @@ -0,0 +1,42 @@
>> +/* { dg-require-effective-target lto } */
>> +/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
>> +/* { dg-require-profiling "-fprofile-generate" } */
>> +/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate --param indir-call-topn-profile=1" } */
>> +
>> +#include <stdio.h>
>> +
>> +typedef int (*fptr) (int);
>> +int
>> +one (int a);
>> +
>> +int
>> +two (int a);
>> +
>> +fptr table[] = {&one, &two};
>> +
>> +int foo ()
>> +{
>> +  int i, x;
>> +  fptr p = &one;
>> +
>> +  x = one (3);
>> +
>> +  for (i = 0; i < 350000000; i++)
>> +    {
>> +      x = (*p) (3);
>> +      p = table[x];
>> +    }
>> +  return x;
>> +}
>> +
>> +int
>> +main()
>> +{
>> +  int x = foo ();
>> +  printf ("done:%d\n", x);
>> +}
>> +
>> +/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* one transformation on insn" "profile_estimate" } } */
>> +/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* two transformation on insn" "profile_estimate" } } */
>> +
>> +
>> diff --git a/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
>> new file mode 100644
>> index 00000000000..951bc7ddd19
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
>> @@ -0,0 +1,38 @@
>> +/* { dg-require-profiling "-fprofile-generate" } */
>> +/* { dg-options "-O2 -fdump-ipa-profile --param indir-call-topn-profile=1" } */
>> +
>> +#include <stdio.h>
>> +
>> +typedef int (*fptr) (int);
>> +int
>> +one (int a)
>> +{
>> +  return 1;
>> +}
>> +
>> +int
>> +two (int a)
>> +{
>> +  return 0;
>> +}
>> +
>> +fptr table[] = {&one, &two};
>> +
>> +int
>> +main()
>> +{
>> +  int i, x;
>> +  fptr p = &one;
>> +
>> +  one (3);
>> +
>> +  for (i = 0; i < 350000000; i++)
>> +    {
>> +      x = (*p) (3);
>> +      p = table[x];
>> +    }
>> +  printf ("done:%d\n", x);
>> +}
>> +
>> +/* { dg-final-use-not-autofdo { scan-ipa-dump "Indirect call -> direct call.* one transformation on insn" "profile" } } */
>> +/* { dg-final-use-not-autofdo { scan-ipa-dump "Indirect call -> direct call.* two transformation on insn" "profile" } } */
>> diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
>> index b9c1a3b1456..fe3e172fbd1 100644
>> --- a/gcc/tree-inline.c
>> +++ b/gcc/tree-inline.c
>> @@ -2167,6 +2167,25 @@ copy_bb (copy_body_data *id, basic_block bb,
>>   
>>   			  gcc_assert (!edge->indirect_unknown_callee);
>>   			  old_edge->speculative_call_info (direct, indirect, ref);
>> +			  while (old_edge->next_callee
>> +				 && old_edge->next_callee->speculative
>> +				 && indirect->has_multiple_indirect_call_p ())
>> +			    {
>> +			      /* Some speculative calls may contain more than
>> +				 one direct target, loop iterate it to clone all
>> +				 related direct edges before cloning the related
>> +				 indirect edge.  */
>> +			      id->dst_node->clone_reference (ref, stmt);
>> +
>> +			      edge = old_edge->next_callee;
>> +			      edge = edge->clone (id->dst_node, call_stmt,
>> +						  gimple_uid (stmt), num, den,
>> +						  true);
>> +			      old_edge = old_edge->next_callee;
>> +			      gcc_assert (!edge->indirect_unknown_callee);
>> +			      old_edge->speculative_call_info (direct, indirect,
>> +							       ref);
>> +			    }
>>   
>>   			  profile_count indir_cnt = indirect->count;
>>   			  indirect = indirect->clone (id->dst_node, call_stmt,
>> diff --git a/gcc/tree-profile.c b/gcc/tree-profile.c
>> index 4c1ead5781f..ef7748668f8 100644
>> --- a/gcc/tree-profile.c
>> +++ b/gcc/tree-profile.c
>> @@ -74,8 +74,8 @@ static GTY(()) tree ic_tuple_callee_field;
>>   /* Do initialization work for the edge profiler.  */
>>   
>>   /* Add code:
>> -   __thread gcov*	__gcov_indirect_call_counters; // pointer to actual counter
>> -   __thread void*	__gcov_indirect_call_callee; // actual callee address
>> +   __thread gcov*	__gcov_indirect_call.counters; // pointer to actual counter
>> +   __thread void*	__gcov_indirect_call.callee; // actual callee address
>>      __thread int __gcov_function_counter; // time profiler function counter
>>   */
>>   static void
>> @@ -382,7 +382,7 @@ gimple_gen_ic_profiler (histogram_value value, unsigned tag)
>>         f_1 = foo;
>>         __gcov_indirect_call.counters = &__gcov4.main[0];
>>         PROF_9 = f_1;
>> -      __gcov_indirect_call_callee = PROF_9;
>> +      __gcov_indirect_call.callee = PROF_9;
>>         _4 = f_1 ();
>>      */
>>   
>> @@ -445,11 +445,11 @@ gimple_gen_ic_func_profiler (void)
>>   
>>     /* Insert code:
>>   
>> -     if (__gcov_indirect_call_callee != NULL)
>> +     if (__gcov_indirect_call.callee != NULL)
>>          __gcov_indirect_call_profiler_v3 (profile_id, &current_function_decl);
>>   
>>        The function __gcov_indirect_call_profiler_v3 is responsible for
>> -     resetting __gcov_indirect_call_callee to NULL.  */
>> +     resetting __gcov_indirect_call.callee to NULL.  */
>>   
>>     gimple_stmt_iterator gsi = gsi_start_bb (cond_bb);
>>     void0 = build_int_cst (ptr_type_node, 0);
>> @@ -891,7 +891,7 @@ pass_ipa_tree_profile::gate (function *)
>>   {
>>     /* When profile instrumentation, use or test coverage shall be performed.
>>        But for AutoFDO, this there is no instrumentation, thus this pass is
>> -     diabled.  */
>> +     disabled.  */
>>     return (!in_lto_p && !flag_auto_profile
>>   	  && (flag_branch_probabilities || flag_test_coverage
>>   	      || profile_arc_flag));
>> diff --git a/gcc/value-prof.c b/gcc/value-prof.c
>> index 55ea0973a03..0588df0fce9 100644
>> --- a/gcc/value-prof.c
>> +++ b/gcc/value-prof.c
>> @@ -1406,11 +1406,10 @@ gimple_ic (gcall *icall_stmt, struct cgraph_node *direct_call,
>>     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:
>> - */
>> +/* There maybe multiple indirect targets in histogram.  Check every
>> +   indirect/virtual call if callee function exists, if not exist, leave it to
>> +   LTO stage for later process.  Modify code of this indirect call to an if-else
>> +   structure in ipa-profile finally.  */
>>   
>>   static bool
>>   gimple_ic_transform (gimple_stmt_iterator *gsi)
>> @@ -1434,48 +1433,57 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
>>     if (!histogram)
>>       return false;
> 
> The function is not correct, note that the function can now return true
> when this transformation happens:
> 		  "Indirect call -> direct call from other "
> 		  "module %T=> %i (will resolve only with LTO)\n",
> 
> Current trunk returns false in that case.
> 
>>   
>> -  if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
>> -				  &count, &all))
>> -    return false;
>> +  count = 0;
>> +  all = histogram->hvalue.counters[0];
>>   
>> -  if (4 * count <= 3 * all)
>> -    return false;
>> +  for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
>> +    {
>> +      if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
>> +				      &count, &all, j))
>> +	continue;
> 
> You should break here as get_nth_most_common_value (..., j + 1) will also return
> false.
> 
>>   
>> -  direct_call = find_func_by_profile_id ((int)val);
>> +      /* Minimum probability.  should be higher than 25%.  */
>> +      if (4 * count <= all)
>> +	continue;
> 
> You can break here as well.
> 
> Thank you,
> Martin
> 
>>   
>> -  if (direct_call == NULL)
>> -    {
>> -      if (val)
>> +      direct_call = find_func_by_profile_id ((int) val);
>> +
>> +      if (direct_call == NULL)
>> +	{
>> +	  if (val)
>> +	    {
>> +	      if (dump_enabled_p ())
>> +		dump_printf_loc (
>> +		  MSG_MISSED_OPTIMIZATION, stmt,
>> +		  "Indirect call -> direct call from other "
>> +		  "module %T=> %i (will resolve only with LTO)\n",
>> +		  gimple_call_fn (stmt), (int) val);
>> +	    }
>> +	  continue;
>> +	}
>> +
>> +      if (!check_ic_target (stmt, direct_call))
>>   	{
>>   	  if (dump_enabled_p ())
>> -	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
>> -			     "Indirect call -> direct call from other "
>> -			     "module %T=> %i (will resolve only with LTO)\n",
>> -			     gimple_call_fn (stmt), (int)val);
>> +	    dump_printf_loc (
>> +	      MSG_MISSED_OPTIMIZATION, stmt,
>> +	      "Indirect call -> direct call %T => %T "
>> +	      "transformation skipped because of type mismatch: %G",
>> +	      gimple_call_fn (stmt), direct_call->decl, stmt);
>> +	  gimple_remove_histogram_value (cfun, stmt, histogram);
>> +	  return false;
>>   	}
>> -      return false;
>> -    }
>>   
>> -  if (!check_ic_target (stmt, direct_call))
>> -    {
>>         if (dump_enabled_p ())
>> -	dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
>> -			 "Indirect call -> direct call %T => %T "
>> -			 "transformation skipped because of type mismatch: %G",
>> -			 gimple_call_fn (stmt), direct_call->decl, stmt);
>> -      gimple_remove_histogram_value (cfun, stmt, histogram);
>> -      return false;
>> -    }
>> -
>> -  if (dump_enabled_p ())
>> -    {
>> -      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
>> -		       "Indirect call -> direct call "
>> -		       "%T => %T transformation on insn postponed\n",
>> -		       gimple_call_fn (stmt), direct_call->decl);
>> -      dump_printf_loc (MSG_NOTE, stmt,
>> -		       "hist->count %" PRId64
>> -		       " hist->all %" PRId64"\n", count, all);
>> +	{
>> +	  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
>> +			   "Indirect call -> direct call "
>> +			   "%T => %T transformation on insn postponed\n",
>> +			   gimple_call_fn (stmt), direct_call->decl);
>> +	  dump_printf_loc (MSG_NOTE, stmt,
>> +			   "hist->count %" PRId64 " hist->all %" PRId64 "\n",
>> +			   count, all);
>> +	}
>>       }
>>   
>>     return true;
>>
> 

[-- Attachment #2: v5-0001-Missed-function-specialization-partial-devirtuali.patch --]
[-- Type: text/plain, Size: 42782 bytes --]

From bf8750f8564991e9a87e3b290ca42b8e88683c20 Mon Sep 17 00:00:00 2001
From: Xiong Hu Luo <luoxhu@linux.ibm.com>
Date: Wed, 24 Apr 2019 00:10:44 -0500
Subject: [PATCH v5] Missed function specialization + partial devirtualization

v5 Changes:
 1. Use constructor for indirect_target_info.
 2. Use vec::is_empty.
 3. Remove deprecated option in test case.  Fix failures.
 4. Fix logic issue in gimple_ic_transform.
v4 Changes:
 1. Rebase to trunk.
 2. Remove num_of_ics and use vector's length to avoid reduncancy.
 3. Update the code in ipa-profile.c to improve review feasibility.
 4. Add function has_indirect_call_p and has_multiple_indirect_call_p.
 5. For parameter control, I will leave it to next patch as it is a
    relative independent function.  Currently, maximum number of
    promotions is GCOV_TOPN_VALUES as only 4 profiling value limited
    from profile-generate, therefore minimum probability is adjusted to
    25% in value-prof.c, it was 75% also by hard code for single
    indirect target.  No control to minimal number of edge
    executions yet.  What's more, this patch is a bit large now.

This patch aims to fix PR69678 caused by PGO indirect call profiling
performance issues.
The bug that profiling data is never working was fixed by Martin's pull
back of topN patches, performance got GEOMEAN ~1% improvement(+24% for
511.povray_r specifically).
Still, currently the default profile only generates SINGLE indirect target
that called more than 75%.  This patch leverages MULTIPLE indirect
targets use in LTO-WPA and LTO-LTRANS stage, as a result, function
specialization, profiling, partial devirtualization, inlining and
cloning could be done successfully based on it.
Performance can get improved from 0.70 sec to 0.38 sec on simple tests.
Details are:
  1.  PGO with topn is enabled by default now, but only one indirect
  target edge will be generated in ipa-profile pass, so add variables to enable
  multiple speculative edges through passes, speculative_id will record the
  direct edge index bind to the indirect edge, indirect_call_targets length
  records how many direct edges owned by the indirect edge, postpone gimple_ic
  to ipa-profile like default as inline pass will decide whether it is benefit
  to transform indirect call.
  2.  Use speculative_id to track and search the reference node matched
  with the direct edge's callee for multiple targets.  Actually, it is the
  caller's responsibility to handle the direct edges mapped to same indirect
  edge.  speculative_call_info will return one of the direct edge specified,
  this will leverage current IPA edge process framework mostly.
  3.  Enable LTO WPA/LTRANS stage multiple indirect call targets analysis for
  profile full support in ipa passes and cgraph_edge functions.  speculative_id
  can be set by make_speculative id when multiple targets are binded to
  one indirect edge, and cloned if new edge is cloned.  speculative_id
  is streamed out and stream int by lto like lto_stmt_uid.
  4.  Add 1 in module testcase and 2 cross module testcases.
  5.  Bootstrap and regression test passed on Power8-LE.  No function
  and performance regression for SPEC2017.

gcc/ChangeLog

	2019-09-25  Xiong Hu Luo  <luoxhu@linux.ibm.com>

	PR ipa/69678
	* cgraph.c (symbol_table::create_edge): Init speculative_id.
	(cgraph_edge::make_speculative): Add param for setting speculative_id.
	(cgraph_edge::speculative_call_info): Find reference by
	speculative_id for multiple indirect targets.
	(cgraph_edge::resolve_speculation): Decrease the speculations
	for indirect edge, drop it's speculative if not direct target
	left.
	(cgraph_edge::redirect_call_stmt_to_callee): Likewise.
	(cgraph_node::verify_node): Don't report error if speculative
	edge not include statement.
	(cgraph_edge::has_multiple_indirect_call_p): New function.
	(cgraph_edge::has_indirect_call_p): New function.
	* cgraph.h (struct indirect_target_info): New struct.
	(indirect_call_targets): New vector variable.
	(make_speculative): Add param for setting speculative_id.
	(cgraph_edge::has_multiple_indirect_call_p): New declare.
	(cgraph_edge::has_indirect_call_p): New declare.
	(speculative_id): New variable.
	* cgraphclones.c (cgraph_node::create_clone): Clone speculative_id.
	* ipa-inline.c (inline_small_functions): Fix iterator update.
	* ipa-profile.c (ipa_profile_generate_summary): Add indirect
	multiple targets logic.
	(ipa_profile): Likewise.
	* ipa-ref.h (speculative_id): New variable.
	* ipa.c (process_references): Fix typo.
	* lto-cgraph.c (lto_output_edge): Add indirect multiple targets
	logic.  Stream out speculative_id.
	(input_edge): Likewise.
	* predict.c (dump_prediction): Revome edges count assert to be
	precise.
	* symtab.c (symtab_node::create_reference): Init speculative_id.
	(symtab_node::clone_references): Clone speculative_id.
	(symtab_node::clone_referring): Clone speculative_id.
	(symtab_node::clone_reference): Clone speculative_id.
	(symtab_node::clear_stmts_in_references): Clear speculative_id.
	* tree-inline.c (copy_bb): Duplicate all the speculative edges
	if indirect call contains multiple speculative targets.
	* tree-profile.c (gimple_gen_ic_profiler): Use the new variable
	__gcov_indirect_call.counters and __gcov_indirect_call.callee.
	(gimple_gen_ic_func_profiler): Likewise.
	(pass_ipa_tree_profile::gate): Fix comment typos.
	* value-prof.c  (gimple_ic_transform): Handle topn case.
	Fix comment typos.

gcc/testsuite/ChangeLog

	2019-09-25  Xiong Hu Luo  <luoxhu@linux.ibm.com>

	PR ipa/69678
	* gcc.dg/tree-prof/indir-call-prof-topn.c: New testcase.
	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c: New testcase.
	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c: New testcase.
	* gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c: New testcase.
	* lib/scandump.exp: Dump executable file name.
	* lib/scanwpaipa.exp: New scan-pgo-wap-ipa-dump.
---
 gcc/cgraph.c                                  | 90 ++++++++++++++++-
 gcc/cgraph.h                                  | 35 ++++++-
 gcc/cgraphclones.c                            |  1 +
 gcc/ipa-inline.c                              | 15 +--
 gcc/ipa-profile.c                             | 97 +++++++++++++------
 gcc/ipa-ref.h                                 |  1 +
 gcc/ipa.c                                     |  2 +-
 gcc/lto-cgraph.c                              | 55 +++++++++--
 gcc/predict.c                                 |  1 -
 gcc/symtab.c                                  |  5 +
 .../tree-prof/crossmodule-indir-call-topn-1.c | 33 +++++++
 .../crossmodule-indir-call-topn-1a.c          | 22 +++++
 .../tree-prof/crossmodule-indir-call-topn-2.c | 40 ++++++++
 .../gcc.dg/tree-prof/indir-call-prof-topn.c   | 37 +++++++
 gcc/testsuite/lib/scandump.exp                |  1 +
 gcc/testsuite/lib/scanwpaipa.exp              | 23 +++++
 gcc/tree-inline.c                             | 19 ++++
 gcc/tree-profile.c                            | 12 +--
 gcc/value-prof.c                              | 86 ++++++++--------
 19 files changed, 475 insertions(+), 100 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c

diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index 331b363c175..8ede0e4df21 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -860,6 +860,7 @@ symbol_table::create_edge (cgraph_node *caller, cgraph_node *callee,
   edge->prev_callee = NULL;
   edge->next_callee = NULL;
   edge->lto_stmt_uid = 0;
+  edge->speculative_id = 0;
 
   edge->count = count;
 
@@ -1051,7 +1052,8 @@ cgraph_edge::remove (void)
    Return direct edge created.  */
 
 cgraph_edge *
-cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
+cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count,
+			       unsigned int speculative_id)
 {
   cgraph_node *n = caller;
   ipa_ref *ref = NULL;
@@ -1069,11 +1071,13 @@ cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
   else
     e2->can_throw_external = can_throw_external;
   e2->lto_stmt_uid = lto_stmt_uid;
+  e2->speculative_id = speculative_id;
   e2->in_polymorphic_cdtor = in_polymorphic_cdtor;
   count -= e2->count;
   symtab->call_edge_duplication_hooks (this, e2);
   ref = n->create_reference (n2, IPA_REF_ADDR, call_stmt);
   ref->lto_stmt_uid = lto_stmt_uid;
+  ref->speculative_id = speculative_id;
   ref->speculative = speculative;
   n2->mark_address_taken ();
   return e2;
@@ -1087,6 +1091,38 @@ cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
    call) and if one of them exists, all of them must exist.
 
    Given speculative call edge, return all three components.
+
+   For some indirect edge, it may maps to multiple direct edges, i.e.  1:N.
+   check the speculative_id to return all the three components for specified
+   direct edge or indirect edge.
+   If input is indirect, caller of this function will get the direct edge one by
+   one, get_edge will just return one of the direct edge mapped to the indirect
+   edge, the returned direct edge will be resolved or redirected by the caller,
+   then number of indirect calls (speculations) is deceased in each access.
+   If input is direct, this function will get the indirect edge and reference
+   with matched speculative_id, the returned edge will also be resolved or
+   redirected, decrease the speculations accordingly.
+   Speculations of indirect edge will be dropped only if all direct edges
+   be handled.
+
+   e.g.  for indirect edge E statement "call call_dest":
+
+   Redirect N3 after redirected N2:
+
+   if (call_dest == N2)
+     n2 ();
+   else if (call_dest == N3)
+     n3 ();
+   else
+     call call_dest
+
+   Resolve N3 and only redirect N2:
+
+   if (call_dest == N2)
+     n2 ();
+   else
+     call call_dest
+
  */
 
 void
@@ -1126,7 +1162,7 @@ cgraph_edge::speculative_call_info (cgraph_edge *&direct,
 
   reference = NULL;
   for (i = 0; e->caller->iterate_reference (i, ref); i++)
-    if (ref->speculative
+    if (ref->speculative && ref->speculative_id == e->speculative_id
 	&& ((ref->stmt && ref->stmt == e->call_stmt)
 	    || (!ref->stmt && ref->lto_stmt_uid == e->lto_stmt_uid)))
       {
@@ -1187,7 +1223,21 @@ cgraph_edge::resolve_speculation (tree callee_decl)
          in the functions inlined through it.  */
     }
   edge->count += e2->count;
-  edge->speculative = false;
+  /* edge is indirect, e2 is direct.  If edge contains multiple speculations,
+     remove one of speculations for this indirect edge, then if edge still
+     contains direct target, keep the speculation, next direct target
+     will continue use it.  Give up speculation completely if no direct
+     target is left for this indirect edge.  */
+  if (edge->has_indirect_call_p ())
+    {
+      /* As the direct targets are sorted by decrease, delete the first target
+	 when it is resolved.  */
+      edge->indirect_info->indirect_call_targets->ordered_remove (0);
+      if (edge->indirect_info->indirect_call_targets->is_empty ())
+	edge->speculative = false;
+    }
+  else
+    edge->speculative = false;
   e2->speculative = false;
   ref->remove_reference ();
   if (e2->indirect_unknown_callee || e2->inline_failed)
@@ -1321,7 +1371,21 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
 	  e->caller->set_call_stmt_including_clones (e->call_stmt, new_stmt,
 						     false);
 	  e->count = gimple_bb (e->call_stmt)->count;
-	  e2->speculative = false;
+	  /* edge is direct, e2 is indirect here.  If e2 contains multiple
+	     speculations, remove one of speculations for this indirect edge,
+	     then if e2 still contains direct target, keep the speculation,
+	     next direct target will continue use it.  Give up speculation
+	     completely if no direct target is left for this indirect e2.  */
+	  if (e2->has_indirect_call_p ())
+	    {
+	      /* As the direct targets are sorted by decrease, delete the first
+		 target when it is redirected.  */
+	      e2->indirect_info->indirect_call_targets->ordered_remove (0);
+	      if (e2->indirect_info->indirect_call_targets->is_empty ())
+		e2->speculative = false;
+	    }
+	  else
+	    e2->speculative = false;
 	  e2->count = gimple_bb (e2->call_stmt)->count;
 	  ref->speculative = false;
 	  ref->stmt = NULL;
@@ -3376,7 +3440,7 @@ cgraph_node::verify_node (void)
 
       for (e = callees; e; e = e->next_callee)
 	{
-	  if (!e->aux)
+	  if (!e->aux && !e->speculative)
 	    {
 	      error ("edge %s->%s has no corresponding call_stmt",
 		     identifier_to_locale (e->caller->name ()),
@@ -3803,6 +3867,22 @@ cgraph_edge::possibly_call_in_translation_unit_p (void)
   return node->get_availability () >= AVAIL_AVAILABLE;
 }
 
+/* Return true if this edge has multiple indirect call targets.  */
+bool
+cgraph_edge::has_multiple_indirect_call_p (void)
+{
+  return (indirect_info && indirect_info->indirect_call_targets
+	  && indirect_info->indirect_call_targets->length () > 1);
+}
+
+/* Return true if this edge has at least one indirect call target.  */
+bool
+cgraph_edge::has_indirect_call_p (void)
+{
+  return (indirect_info && indirect_info->indirect_call_targets
+	  && !indirect_info->indirect_call_targets->is_empty ());
+}
+
 /* A stashed copy of "symtab" for use by selftest::symbol_table_test.
    This needs to be a global so that it can be a GC root, and thus
    prevent the stashed copy from being garbage-collected if the GC runs
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 1da6cab54b0..43187bd7a19 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -1647,6 +1647,22 @@ private:
   void make_speculative (tree otr_type = NULL);
 };
 
+/* Structure containing indirect target information from profile.  */
+
+struct GTY (()) indirect_target_info
+{
+  /* Profile_id of common target obtained from profile.  */
+  unsigned int common_target_id;
+  /* Probability that call will land in function with COMMON_TARGET_ID.  */
+  int common_target_probability;
+
+  indirect_target_info (unsigned int id, int prob)
+  {
+    common_target_id = id;
+    common_target_probability = prob;
+  }
+};
+
 /* Structure containing additional information about an indirect call.  */
 
 class GTY(()) cgraph_indirect_call_info
@@ -1665,10 +1681,9 @@ public:
   int param_index;
   /* ECF flags determined from the caller.  */
   int ecf_flags;
-  /* Profile_id of common target obtrained from profile.  */
-  int common_target_id;
-  /* Probability that call will land in function with COMMON_TARGET_ID.  */
-  int common_target_probability;
+
+  /* An indirect call may contain one or multiple call targets.  */
+  vec<indirect_target_info, va_gc> *indirect_call_targets;
 
   /* Set when the call is a virtual call with the parameter being the
      associated object pointer rather than a simple direct call.  */
@@ -1725,7 +1740,8 @@ public:
   /* Turn edge into speculative call calling N2. Update
      the profile so the direct call is taken COUNT times
      with FREQUENCY.  */
-  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count);
+  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count,
+				 unsigned int speculative_id = 0);
 
    /* Given speculative call edge, return all three components.  */
   void speculative_call_info (cgraph_edge *&direct, cgraph_edge *&indirect,
@@ -1784,6 +1800,12 @@ public:
      be internal to the current translation unit.  */
   bool possibly_call_in_translation_unit_p (void);
 
+  /* Return true if this edge has multiple indirect call targets.  */
+  bool has_multiple_indirect_call_p (void);
+
+  /* Return true if this edge has at least one indirect call target.  */
+  bool has_indirect_call_p (void);
+
   /* Expected number of executions: calculated in profile.c.  */
   profile_count count;
   cgraph_node *caller;
@@ -1803,6 +1825,9 @@ public:
   /* The stmt_uid of call_stmt.  This is used by LTO to recover the call_stmt
      when the function is serialized in.  */
   unsigned int lto_stmt_uid;
+  /* speculative id is used by multiple indirect targets when the function is
+   speculated.  */
+  unsigned int speculative_id;
   /* Whether this edge was made direct by indirect inlining.  */
   unsigned int indirect_inlining_edge : 1;
   /* Whether this edge describes an indirect call with an undetermined
diff --git a/gcc/cgraphclones.c b/gcc/cgraphclones.c
index 909407b9a71..0c9160d9240 100644
--- a/gcc/cgraphclones.c
+++ b/gcc/cgraphclones.c
@@ -128,6 +128,7 @@ cgraph_edge::clone (cgraph_node *n, gcall *call_stmt, unsigned stmt_uid,
   new_edge->inline_failed = inline_failed;
   new_edge->indirect_inlining_edge = indirect_inlining_edge;
   new_edge->lto_stmt_uid = stmt_uid;
+  new_edge->speculative_id = speculative_id;
   /* Clone flags that depend on call_stmt availability manually.  */
   new_edge->can_throw_external = can_throw_external;
   new_edge->call_stmt_cannot_inline_p = call_stmt_cannot_inline_p;
diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
index b62d280eb25..6136214f9ac 100644
--- a/gcc/ipa-inline.c
+++ b/gcc/ipa-inline.c
@@ -1881,12 +1881,15 @@ inline_small_functions (void)
 	}
       if (has_speculative)
 	for (edge = node->callees; edge; edge = next)
-	  if (edge->speculative && !speculation_useful_p (edge,
-							  edge->aux != NULL))
-	    {
-	      edge->resolve_speculation ();
-	      update = true;
-	    }
+	  {
+	    next = edge->next_callee;
+	    if (edge->speculative
+		&& !speculation_useful_p (edge, edge->aux != NULL))
+	      {
+		edge->resolve_speculation ();
+		update = true;
+	      }
+	  }
       if (update)
 	{
 	  struct cgraph_node *where = node->global.inlined_to
diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
index 970dba39c80..13bd05a1061 100644
--- a/gcc/ipa-profile.c
+++ b/gcc/ipa-profile.c
@@ -192,23 +192,34 @@ ipa_profile_generate_summary (void)
 		  if (h)
 		    {
 		      gcov_type val, count, all;
-		      if (get_nth_most_common_value (NULL, "indirect call", h,
-						     &val, &count, &all))
+		      struct cgraph_edge *e = node->get_edge (stmt);
+		      if (e && !e->indirect_unknown_callee)
+			continue;
+
+		      for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
 			{
-			  struct cgraph_edge * e = node->get_edge (stmt);
-			  if (e && !e->indirect_unknown_callee)
+			  if (!get_nth_most_common_value (NULL, "indirect call",
+							  h, &val, &count, &all,
+							  j))
+			    continue;
+
+			  if (val == 0)
 			    continue;
 
-			  e->indirect_info->common_target_id = val;
-			  e->indirect_info->common_target_probability
-			    = GCOV_COMPUTE_SCALE (count, all);
-			  if (e->indirect_info->common_target_probability > REG_BR_PROB_BASE)
+			  indirect_target_info item (val,
+						     GCOV_COMPUTE_SCALE (count,
+									 all));
+			  if (item.common_target_probability > REG_BR_PROB_BASE)
 			    {
 			      if (dump_file)
-				fprintf (dump_file, "Probability capped to 1\n");
-			      e->indirect_info->common_target_probability = REG_BR_PROB_BASE;
+				fprintf (dump_file,
+					 "Probability capped to 1\n");
+			      item.common_target_probability = REG_BR_PROB_BASE;
 			    }
+			  vec_safe_push (
+			    e->indirect_info->indirect_call_targets, item);
 			}
+
 		      gimple_remove_histogram_value (DECL_STRUCT_FUNCTION (node->decl),
 						      stmt, h);
 		    }
@@ -492,6 +503,7 @@ ipa_profile (void)
   int nindirect = 0, ncommon = 0, nunknown = 0, nuseless = 0, nconverted = 0;
   int nmismatch = 0, nimpossible = 0;
   bool node_map_initialized = false;
+  gcov_type threshold;
 
   if (dump_file)
     dump_histogram (dump_file, histogram);
@@ -500,14 +512,12 @@ ipa_profile (void)
       overall_time += histogram[i]->count * histogram[i]->time;
       overall_size += histogram[i]->size;
     }
+  threshold = 0;
   if (overall_time)
     {
-      gcov_type threshold;
-
       gcc_assert (overall_size);
 
       cutoff = (overall_time * PARAM_VALUE (HOT_BB_COUNT_WS_PERMILLE) + 500) / 1000;
-      threshold = 0;
       for (i = 0; cumulated < cutoff; i++)
 	{
 	  cumulated += histogram[i]->count * histogram[i]->time;
@@ -543,7 +553,7 @@ ipa_profile (void)
   histogram.release ();
   histogram_pool.release ();
 
-  /* Produce speculative calls: we saved common traget from porfiling into
+  /* Produce speculative calls: we saved common target from profiling into
      e->common_target_id.  Now, at link time, we can look up corresponding
      function node and produce speculative call.  */
 
@@ -558,13 +568,37 @@ ipa_profile (void)
 	{
 	  if (n->count.initialized_p ())
 	    nindirect++;
-	  if (e->indirect_info->common_target_id)
+	  if (e->has_indirect_call_p ())
 	    {
 	      if (!node_map_initialized)
-	        init_node_map (false);
+		init_node_map (false);
 	      node_map_initialized = true;
 	      ncommon++;
-	      n2 = find_func_by_profile_id (e->indirect_info->common_target_id);
+
+	      if (in_lto_p)
+		{
+		  if (dump_file)
+		    {
+		      fprintf (dump_file,
+			       "Updating hotness threshold in LTO mode.\n");
+		      fprintf (dump_file, "Updated min count: %" PRId64 "\n",
+			       (int64_t) threshold);
+		    }
+		  set_hot_bb_threshold (threshold
+		    / e->indirect_info->indirect_call_targets->length ());
+		}
+
+	      unsigned speculative_id = 0;
+	      indirect_target_info *item;
+	      /* The code below is not formatted yet for review convenience.
+		 Move to a seprate small function is not easy as too many local
+		 variables used in it.  Need format and remove this comments
+		 once got approved.  */
+	      FOR_EACH_VEC_SAFE_ELT (e->indirect_info->indirect_call_targets, i,
+				     item)
+	       {
+	      bool speculative_found = false;
+	      n2 = find_func_by_profile_id (item->common_target_id);
 	      if (n2)
 		{
 		  if (dump_file)
@@ -573,11 +607,10 @@ ipa_profile (void)
 			       " other module %s => %s, prob %3.2f\n",
 			       n->dump_name (),
 			       n2->dump_name (),
-			       e->indirect_info->common_target_probability
-			       / (float)REG_BR_PROB_BASE);
+			       item->common_target_probability
+				 / (float) REG_BR_PROB_BASE);
 		    }
-		  if (e->indirect_info->common_target_probability
-		      < REG_BR_PROB_BASE / 2)
+		  if (item->common_target_probability < REG_BR_PROB_BASE / 2)
 		    {
 		      nuseless++;
 		      if (dump_file)
@@ -613,7 +646,7 @@ ipa_profile (void)
 		      if (dump_file)
 			fprintf (dump_file,
 				 "Not speculating: "
-				 "parameter count mistmatch\n");
+				 "parameter count mismatch\n");
 		    }
 		  else if (e->indirect_info->polymorphic
 			   && !opt_for_fn (n->decl, flag_devirtualize)
@@ -640,20 +673,30 @@ ipa_profile (void)
 			    n2 = alias;
 			}
 		      nconverted++;
-		      e->make_speculative
-			(n2,
-			 e->count.apply_probability
-				     (e->indirect_info->common_target_probability));
+		      e->make_speculative (n2,
+					   e->count.apply_probability (
+					     item->common_target_probability),
+					   speculative_id);
 		      update = true;
+		      speculative_id++;
+		      speculative_found = true;
 		    }
 		}
 	      else
 		{
 		  if (dump_file)
 		    fprintf (dump_file, "Function with profile-id %i not found.\n",
-			     e->indirect_info->common_target_id);
+			     item->common_target_id);
 		  nunknown++;
 		}
+	      if (!speculative_found)
+		{
+		  /* Remove item from indirect_call_targets if no
+		     speculative edge generated, rollback the iteration.  */
+		  e->indirect_info->indirect_call_targets->ordered_remove (i);
+		  i--;
+		}
+	       }
 	    }
 	 }
        if (update)
diff --git a/gcc/ipa-ref.h b/gcc/ipa-ref.h
index 0d8e509c932..3e6562ec9d1 100644
--- a/gcc/ipa-ref.h
+++ b/gcc/ipa-ref.h
@@ -59,6 +59,7 @@ public:
   symtab_node *referred;
   gimple *stmt;
   unsigned int lto_stmt_uid;
+  unsigned int speculative_id;
   unsigned int referred_index;
   ENUM_BITFIELD (ipa_ref_use) use:3;
   unsigned int speculative:1;
diff --git a/gcc/ipa.c b/gcc/ipa.c
index 6b84e1f9bda..a10b0603f14 100644
--- a/gcc/ipa.c
+++ b/gcc/ipa.c
@@ -166,7 +166,7 @@ process_references (symtab_node *snode,
    devirtualization happens.  After inlining still keep their declarations
    around, so we can devirtualize to a direct call.
 
-   Also try to make trivial devirutalization when no or only one target is
+   Also try to make trivial devirtualization when no or only one target is
    possible.  */
 
 static void
diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
index 147975ba869..45f0d4ddc41 100644
--- a/gcc/lto-cgraph.c
+++ b/gcc/lto-cgraph.c
@@ -238,6 +238,7 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
   unsigned int uid;
   intptr_t ref;
   struct bitpack_d bp;
+  unsigned len;
 
   if (edge->indirect_unknown_callee)
     streamer_write_enum (ob->main_stream, LTO_symtab_tags, LTO_symtab_last_tag,
@@ -265,6 +266,7 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
   bp_pack_enum (&bp, cgraph_inline_failed_t,
 	        CIF_N_REASONS, edge->inline_failed);
   bp_pack_var_len_unsigned (&bp, uid);
+  bp_pack_var_len_unsigned (&bp, edge->speculative_id);
   bp_pack_value (&bp, edge->indirect_inlining_edge, 1);
   bp_pack_value (&bp, edge->speculative, 1);
   bp_pack_value (&bp, edge->call_stmt_cannot_inline_p, 1);
@@ -291,11 +293,26 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
   streamer_write_bitpack (&bp);
   if (edge->indirect_unknown_callee)
     {
-      streamer_write_hwi_stream (ob->main_stream,
-			         edge->indirect_info->common_target_id);
-      if (edge->indirect_info->common_target_id)
-	streamer_write_hwi_stream
-	   (ob->main_stream, edge->indirect_info->common_target_probability);
+      indirect_target_info *item;
+      unsigned int i;
+      len = edge->has_indirect_call_p ()
+	      ? edge->indirect_info->indirect_call_targets->length ()
+	      : 0;
+      gcc_assert (len <= GCOV_TOPN_VALUES);
+
+      streamer_write_hwi_stream (ob->main_stream, len);
+
+      if (len)
+	{
+	  FOR_EACH_VEC_SAFE_ELT (edge->indirect_info->indirect_call_targets, i,
+				 item)
+	    {
+	      streamer_write_hwi_stream (ob->main_stream,
+					 item->common_target_id);
+	      streamer_write_hwi_stream (ob->main_stream,
+					 item->common_target_probability);
+	    }
+	}
     }
 }
 
@@ -688,6 +705,7 @@ lto_output_ref (struct lto_simple_output_block *ob, struct ipa_ref *ref,
       if (ref->stmt)
 	uid = gimple_uid (ref->stmt) + 1;
       streamer_write_hwi_stream (ob->main_stream, uid);
+      streamer_write_hwi_stream (ob->main_stream, ref->speculative_id);
     }
 }
 
@@ -1419,7 +1437,10 @@ input_ref (class lto_input_block *ib,
   ref = referring_node->create_reference (node, use);
   ref->speculative = speculative;
   if (is_a <cgraph_node *> (referring_node))
-    ref->lto_stmt_uid = streamer_read_hwi (ib);
+    {
+      ref->lto_stmt_uid = streamer_read_hwi (ib);
+      ref->speculative_id = streamer_read_hwi (ib);
+    }
 }
 
 /* Read an edge from IB.  NODES points to a vector of previously read nodes for
@@ -1433,11 +1454,12 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
 {
   struct cgraph_node *caller, *callee;
   struct cgraph_edge *edge;
-  unsigned int stmt_id;
+  unsigned int stmt_id, speculative_id;
   profile_count count;
   cgraph_inline_failed_t inline_failed;
   struct bitpack_d bp;
   int ecf_flags = 0;
+  unsigned i, len;
 
   caller = dyn_cast<cgraph_node *> (nodes[streamer_read_hwi (ib)]);
   if (caller == NULL || caller->decl == NULL_TREE)
@@ -1457,6 +1479,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
   bp = streamer_read_bitpack (ib);
   inline_failed = bp_unpack_enum (&bp, cgraph_inline_failed_t, CIF_N_REASONS);
   stmt_id = bp_unpack_var_len_unsigned (&bp);
+  speculative_id = bp_unpack_var_len_unsigned (&bp);
 
   if (indirect)
     edge = caller->create_indirect_edge (NULL, 0, count);
@@ -1466,6 +1489,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
   edge->indirect_inlining_edge = bp_unpack_value (&bp, 1);
   edge->speculative = bp_unpack_value (&bp, 1);
   edge->lto_stmt_uid = stmt_id;
+  edge->speculative_id = speculative_id;
   edge->inline_failed = inline_failed;
   edge->call_stmt_cannot_inline_p = bp_unpack_value (&bp, 1);
   edge->can_throw_external = bp_unpack_value (&bp, 1);
@@ -1485,9 +1509,20 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
       if (bp_unpack_value (&bp, 1))
 	ecf_flags |= ECF_RETURNS_TWICE;
       edge->indirect_info->ecf_flags = ecf_flags;
-      edge->indirect_info->common_target_id = streamer_read_hwi (ib);
-      if (edge->indirect_info->common_target_id)
-        edge->indirect_info->common_target_probability = streamer_read_hwi (ib);
+
+      len = streamer_read_hwi (ib);
+
+      gcc_assert (len <= GCOV_TOPN_VALUES);
+
+      if (len)
+	{
+	  for (i = 0; i < len; i++)
+	    {
+	      indirect_target_info item (streamer_read_hwi (ib),
+					 streamer_read_hwi (ib));
+	      vec_safe_push (edge->indirect_info->indirect_call_targets, item);
+	    }
+	}
     }
 }
 
diff --git a/gcc/predict.c b/gcc/predict.c
index 915f0806b11..3f56fa3a74a 100644
--- a/gcc/predict.c
+++ b/gcc/predict.c
@@ -762,7 +762,6 @@ dump_prediction (FILE *file, enum br_predictor predictor, int probability,
       && bb->count.precise_p ()
       && reason == REASON_NONE)
     {
-      gcc_assert (e->count ().precise_p ());
       fprintf (file, ";;heuristics;%s;%" PRId64 ";%" PRId64 ";%.1f;\n",
 	       predictor_info[predictor].name,
 	       bb->count.to_gcov_type (), e->count ().to_gcov_type (),
diff --git a/gcc/symtab.c b/gcc/symtab.c
index ee9723c3453..d4c36fd3e5a 100644
--- a/gcc/symtab.c
+++ b/gcc/symtab.c
@@ -603,6 +603,7 @@ symtab_node::create_reference (symtab_node *referred_node,
   ref->referred = referred_node;
   ref->stmt = stmt;
   ref->lto_stmt_uid = 0;
+  ref->speculative_id = 0;
   ref->use = use_type;
   ref->speculative = 0;
 
@@ -660,6 +661,7 @@ symtab_node::clone_references (symtab_node *node)
       ref2 = create_reference (ref->referred, ref->use, ref->stmt);
       ref2->speculative = speculative;
       ref2->lto_stmt_uid = stmt_uid;
+      ref2->speculative_id = ref->speculative_id;
     }
 }
 
@@ -678,6 +680,7 @@ symtab_node::clone_referring (symtab_node *node)
       ref2 = ref->referring->create_reference (this, ref->use, ref->stmt);
       ref2->speculative = speculative;
       ref2->lto_stmt_uid = stmt_uid;
+      ref2->speculative_id = ref->speculative_id;
     }
 }
 
@@ -693,6 +696,7 @@ symtab_node::clone_reference (ipa_ref *ref, gimple *stmt)
   ref2 = create_reference (ref->referred, ref->use, stmt);
   ref2->speculative = speculative;
   ref2->lto_stmt_uid = stmt_uid;
+  ref2->speculative_id = ref->speculative_id;
   return ref2;
 }
 
@@ -747,6 +751,7 @@ symtab_node::clear_stmts_in_references (void)
       {
 	r->stmt = NULL;
 	r->lto_stmt_uid = 0;
+	r->speculative_id = 0;
       }
 }
 
diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
new file mode 100644
index 00000000000..a13b08cd60e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
@@ -0,0 +1,33 @@
+/* { dg-require-effective-target lto } */
+/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate" } */
+
+#include <stdio.h>
+
+typedef int (*fptr) (int);
+int
+one (int a);
+
+int
+two (int a);
+
+fptr table[] = {&one, &two};
+
+int
+main()
+{
+  int i, x;
+  fptr p = &one;
+
+  x = one (3);
+
+  for (i = 0; i < 350000000; i++)
+    {
+      x = (*p) (3);
+      p = table[x];
+    }
+  printf ("done:%d\n", x);
+}
+
+/* { dg-final-use-not-autofdo { scan-pgo-wpa-ipa-dump "2 \\(200.00%\\) speculations produced." "profile_estimate" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
new file mode 100644
index 00000000000..a8c6e365fb9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
@@ -0,0 +1,22 @@
+/* It seems there is no way to avoid the other source of mulitple
+   source testcase from being compiled independently.  Just avoid
+   error.  */
+#ifdef DOJOB
+int
+one (int a)
+{
+  return 1;
+}
+
+int
+two (int a)
+{
+  return 0;
+}
+#else
+int
+main()
+{
+  return 0;
+}
+#endif
diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
new file mode 100644
index 00000000000..9b996fcf0ed
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
@@ -0,0 +1,40 @@
+/* { dg-require-effective-target lto } */
+/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate" } */
+
+#include <stdio.h>
+
+typedef int (*fptr) (int);
+int
+one (int a);
+
+int
+two (int a);
+
+fptr table[] = {&one, &two};
+
+int foo ()
+{
+  int i, x;
+  fptr p = &one;
+
+  x = one (3);
+
+  for (i = 0; i < 350000000; i++)
+    {
+      x = (*p) (3);
+      p = table[x];
+    }
+  return x;
+}
+
+int
+main()
+{
+  int x = foo ();
+  printf ("done:%d\n", x);
+}
+
+/* { dg-final-use-not-autofdo { scan-pgo-wpa-ipa-dump "2 \\(200.00%\\) speculations produced." "profile_estimate" } } */
+
diff --git a/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
new file mode 100644
index 00000000000..063996c71df
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
@@ -0,0 +1,37 @@
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-options "-O2 -fdump-ipa-profile_estimate" } */
+
+#include <stdio.h>
+
+typedef int (*fptr) (int);
+int
+one (int a)
+{
+  return 1;
+}
+
+int
+two (int a)
+{
+  return 0;
+}
+
+fptr table[] = {&one, &two};
+
+int
+main()
+{
+  int i, x;
+  fptr p = &one;
+
+  one (3);
+
+  for (i = 0; i < 350000000; i++)
+    {
+      x = (*p) (3);
+      p = table[x];
+    }
+  printf ("done:%d\n", x);
+}
+
+/* { dg-final-use-not-autofdo { scan-ipa-dump "2 \\(200.00%\\) speculations produced." "profile_estimate" } } */
diff --git a/gcc/testsuite/lib/scandump.exp b/gcc/testsuite/lib/scandump.exp
index 42f5c01aa60..8b7cd7cfc16 100644
--- a/gcc/testsuite/lib/scandump.exp
+++ b/gcc/testsuite/lib/scandump.exp
@@ -70,6 +70,7 @@ proc scan-dump { args } {
     set output_file "[glob -nocomplain $dumpbase.[lindex $args 2]]"
     if { $output_file == "" } {
 	verbose -log "$testcase: dump file does not exist"
+	verbose -log "dump file: $dumpbase.$suf"
 	unresolved "$testname"
 	return
     }
diff --git a/gcc/testsuite/lib/scanwpaipa.exp b/gcc/testsuite/lib/scanwpaipa.exp
index b5549fd688e..7509ab1b130 100644
--- a/gcc/testsuite/lib/scanwpaipa.exp
+++ b/gcc/testsuite/lib/scanwpaipa.exp
@@ -45,6 +45,29 @@ proc scan-wpa-ipa-dump { args } {
     }
 }
 
+# Argument 0 is the regexp to match
+# Argument 1 is the name of the dumped ipa pass
+# Argument 2 handles expected failures and the like
+proc scan-pgo-wpa-ipa-dump { args } {
+
+    if { [llength $args] < 2 } {
+	error "scan-wpa-ipa-dump: too few arguments"
+	return
+    }
+    if { [llength $args] > 3 } {
+	error "scan-wpa-ipa-dump: too many arguments"
+	return
+    }
+    if { [llength $args] >= 3 } {
+	scan-dump "wpa-ipa" [lindex $args 0] \
+		  "\[0-9\]\[0-9\]\[0-9\]i.[lindex $args 1]" ".x02.wpa" \
+		  [lindex $args 2]
+    } else {
+	scan-dump "wpa-ipa" [lindex $args 0] \
+		  "\[0-9\]\[0-9\]\[0-9\]i.[lindex $args 1]" ".x02.wpa"
+    }
+}
+
 # Call pass if pattern is present given number of times, otherwise fail.
 # Argument 0 is the regexp to match
 # Argument 1 is number of times the regexp must be found
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index e4ae1b058fd..b3ec7f5b99b 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -2195,6 +2195,25 @@ copy_bb (copy_body_data *id, basic_block bb,
 
 			  gcc_assert (!edge->indirect_unknown_callee);
 			  old_edge->speculative_call_info (direct, indirect, ref);
+			  while (old_edge->next_callee
+				 && old_edge->next_callee->speculative
+				 && indirect->has_multiple_indirect_call_p ())
+			    {
+			      /* Some speculative calls may contain more than
+				 one direct target, loop iterate it to clone all
+				 related direct edges before cloning the related
+				 indirect edge.  */
+			      id->dst_node->clone_reference (ref, stmt);
+
+			      edge = old_edge->next_callee;
+			      edge = edge->clone (id->dst_node, call_stmt,
+						  gimple_uid (stmt), num, den,
+						  true);
+			      old_edge = old_edge->next_callee;
+			      gcc_assert (!edge->indirect_unknown_callee);
+			      old_edge->speculative_call_info (direct, indirect,
+							       ref);
+			    }
 
 			  profile_count indir_cnt = indirect->count;
 			  indirect = indirect->clone (id->dst_node, call_stmt,
diff --git a/gcc/tree-profile.c b/gcc/tree-profile.c
index 4c1ead5781f..ef7748668f8 100644
--- a/gcc/tree-profile.c
+++ b/gcc/tree-profile.c
@@ -74,8 +74,8 @@ static GTY(()) tree ic_tuple_callee_field;
 /* Do initialization work for the edge profiler.  */
 
 /* Add code:
-   __thread gcov*	__gcov_indirect_call_counters; // pointer to actual counter
-   __thread void*	__gcov_indirect_call_callee; // actual callee address
+   __thread gcov*	__gcov_indirect_call.counters; // pointer to actual counter
+   __thread void*	__gcov_indirect_call.callee; // actual callee address
    __thread int __gcov_function_counter; // time profiler function counter
 */
 static void
@@ -382,7 +382,7 @@ gimple_gen_ic_profiler (histogram_value value, unsigned tag)
       f_1 = foo;
       __gcov_indirect_call.counters = &__gcov4.main[0];
       PROF_9 = f_1;
-      __gcov_indirect_call_callee = PROF_9;
+      __gcov_indirect_call.callee = PROF_9;
       _4 = f_1 ();
    */
 
@@ -445,11 +445,11 @@ gimple_gen_ic_func_profiler (void)
 
   /* Insert code:
 
-     if (__gcov_indirect_call_callee != NULL)
+     if (__gcov_indirect_call.callee != NULL)
        __gcov_indirect_call_profiler_v3 (profile_id, &current_function_decl);
 
      The function __gcov_indirect_call_profiler_v3 is responsible for
-     resetting __gcov_indirect_call_callee to NULL.  */
+     resetting __gcov_indirect_call.callee to NULL.  */
 
   gimple_stmt_iterator gsi = gsi_start_bb (cond_bb);
   void0 = build_int_cst (ptr_type_node, 0);
@@ -891,7 +891,7 @@ pass_ipa_tree_profile::gate (function *)
 {
   /* When profile instrumentation, use or test coverage shall be performed.
      But for AutoFDO, this there is no instrumentation, thus this pass is
-     diabled.  */
+     disabled.  */
   return (!in_lto_p && !flag_auto_profile
 	  && (flag_branch_probabilities || flag_test_coverage
 	      || profile_arc_flag));
diff --git a/gcc/value-prof.c b/gcc/value-prof.c
index 55ea0973a03..31360d73de8 100644
--- a/gcc/value-prof.c
+++ b/gcc/value-prof.c
@@ -1406,11 +1406,10 @@ gimple_ic (gcall *icall_stmt, struct cgraph_node *direct_call,
   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:
- */
+/* There maybe multiple indirect targets in histogram.  Check every
+   indirect/virtual call if callee function exists, if not exist, leave it to
+   LTO stage for later process.  Modify code of this indirect call to an if-else
+   structure in ipa-profile finally.  */
 
 static bool
 gimple_ic_transform (gimple_stmt_iterator *gsi)
@@ -1434,48 +1433,57 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
   if (!histogram)
     return false;
 
-  if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
-				  &count, &all))
-    return false;
+  count = 0;
+  all = histogram->hvalue.counters[0];
 
-  if (4 * count <= 3 * all)
-    return false;
+  for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
+    {
+      if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
+				      &count, &all, j))
+	return false;
+
+      /* Minimum probability.  should be higher than 25%.  */
+      if (4 * count <= all)
+	return false;
 
-  direct_call = find_func_by_profile_id ((int)val);
+      direct_call = find_func_by_profile_id ((int) val);
 
-  if (direct_call == NULL)
-    {
-      if (val)
+      if (direct_call == NULL)
+	{
+	  if (val)
+	    {
+	      if (dump_enabled_p ())
+		dump_printf_loc (
+		  MSG_MISSED_OPTIMIZATION, stmt,
+		  "Indirect call -> direct call from other "
+		  "module %T=> %i (will resolve only with LTO)\n",
+		  gimple_call_fn (stmt), (int) val);
+	    }
+	  return false;
+	}
+
+      if (!check_ic_target (stmt, direct_call))
 	{
 	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
-			     "Indirect call -> direct call from other "
-			     "module %T=> %i (will resolve only with LTO)\n",
-			     gimple_call_fn (stmt), (int)val);
+	    dump_printf_loc (
+	      MSG_MISSED_OPTIMIZATION, stmt,
+	      "Indirect call -> direct call %T => %T "
+	      "transformation skipped because of type mismatch: %G",
+	      gimple_call_fn (stmt), direct_call->decl, stmt);
+	  gimple_remove_histogram_value (cfun, stmt, histogram);
+	  return false;
 	}
-      return false;
-    }
 
-  if (!check_ic_target (stmt, direct_call))
-    {
       if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
-			 "Indirect call -> direct call %T => %T "
-			 "transformation skipped because of type mismatch: %G",
-			 gimple_call_fn (stmt), direct_call->decl, stmt);
-      gimple_remove_histogram_value (cfun, stmt, histogram);
-      return false;
-    }
-
-  if (dump_enabled_p ())
-    {
-      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
-		       "Indirect call -> direct call "
-		       "%T => %T transformation on insn postponed\n",
-		       gimple_call_fn (stmt), direct_call->decl);
-      dump_printf_loc (MSG_NOTE, stmt,
-		       "hist->count %" PRId64
-		       " hist->all %" PRId64"\n", count, all);
+	{
+	  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
+			   "Indirect call -> direct call "
+			   "%T => %T transformation on insn postponed\n",
+			   gimple_call_fn (stmt), direct_call->decl);
+	  dump_printf_loc (MSG_NOTE, stmt,
+			   "hist->count %" PRId64 " hist->all %" PRId64 "\n",
+			   count, all);
+	}
     }
 
   return true;
-- 
2.21.0.777.g83232e3864


^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v4] Missed function specialization + partial devirtualization
  2019-09-26  5:24           ` luoxhu
@ 2019-09-26  8:36             ` Martin Liška
  2019-09-27  7:13               ` [PATCH v5] " luoxhu
  0 siblings, 1 reply; 38+ messages in thread
From: Martin Liška @ 2019-09-26  8:36 UTC (permalink / raw)
  To: luoxhu, gcc-patches; +Cc: hubicka, segher, wschmidt

[-- Attachment #1: Type: text/plain, Size: 68098 bytes --]

On 9/26/19 7:23 AM, luoxhu wrote:
> Thanks Martin,
> 
> 
> On 2019/9/25 18:57, Martin Liška wrote:
>> On 9/25/19 5:45 AM, luoxhu wrote:
>>> Hi,
>>>
>>> Sorry for replying so late due to cauldron conference and other LTO issues
>>> I was working on.
>>
>> Hello.
>>
>> That's fine, we still have plenty of time for patch review.
>>
>> Not fixed issues which I reported in v3 (and still valid in v4):
>> - please come up with indirect_target_info::indirect_target_info and use it
> Sorry for miss out.

Hello.

Sure, please use a contructor initialization (see my patch).

> 
> 
>> - do you need to stream out indirect_call_targets when common_target_id == 0?
> 
> No need to stream out items with common_target_id == 0, removed the if condition in lto-cgraph.c.

Fine. Do we have a guarantee that item->common_target_id is always != 0? Please put there an assert.

> 
>>
>> Then I'm suggesting to use vec::is_empty (please see my patch).
> OK.  But has_multiple_indirect_call_p should return different than has_indirect_call_p as it checks more that one targets?

Sure, that was mistake in my patch from previous reply.

> 
> gcc/cgraph.c
> /* Return true if this edge has multiple indirect call targets.  */
>  bool
>  cgraph_edge::has_multiple_indirect_call_p (void)
>  {
> -  return indirect_info && indirect_info->indirect_call_targets
> -        && indirect_info->indirect_call_targets->length () > 1;
> +  return (indirect_info && indirect_info->indirect_call_targets
> +         && indirect_info->indirect_call_targets->length () > 1);
>  }
> 
>  /* Return true if this edge has at least one indirect call target.  */
>  bool
>  cgraph_edge::has_indirect_call_p (void)
>  {
> -  return indirect_info && indirect_info->indirect_call_targets
> -        && indirect_info->indirect_call_targets->length ();
> +  return (indirect_info && indirect_info->indirect_call_targets
> +         && !indirect_info->indirect_call_targets->is_empty ());
>  }
> 
>>
>> I see following failures for the tests provided:
>> FAIL: gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c compilation,  -fprofile-generate -D_PROFILE_GENERATE
>> FAIL: gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c compilation,  -fprofile-generate -D_PROFILE_GENERATE
>> FAIL: gcc.dg/tree-prof/indir-call-prof-topn.c compilation,  -fprofile-generate -D_PROFILE_GENERATE
> 
> Sorry that I forgot to remove the deprecated build option in the 3 cases
> (also updated the scan exp check):
> -/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate --param indir-call-topn-profile=1" } */
> +/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate" } */
> 
> 
> The new patch is attached.  Thanks.

Hm, looking at the gimple_ic_transform function. I think the function should always
return false as it never does a GIMPLE transformation.

Apart from that, I'm fine with the patch. Note that I'm not the maintainer, but I bet we simplified
the patch review to Honza significantly.

Last missing piece is probably the update ChangeLog.

Thank you for working on that,
Martin

> 
> 
> Xiong Hu
> 
>>
>> Next comments follow directly in the email body:
>>
>>>
>>> v4 Changes:
>>>   1. Rebase to trunk.
>>>   2. Remove num_of_ics and use vector's length to avoid redundancy.
>>>   3. Update the code in ipa-profile.c to improve review feasibility.
>>>   4. Add function has_indirect_call_p and has_multiple_indirect_call_p.
>>>   5. For parameter control, I will leave it to next patch as it is a
>>>      relative independent function.  Currently, maximum number of
>>>      promotions is GCOV_TOPN_VALUES as only 4 profiling value limited
>>>      from profile-generate, therefore minimum probability is adjusted to
>>>      25% in value-prof.c, it was 75% also by hard code for single
>>>      indirect target.  No control to minimal number of edge
>>>      executions yet.  What's more, this patch is a bit large now.
>>>
>>> This patch aims to fix PR69678 caused by PGO indirect call profiling
>>> performance issues.
>>> The bug that profiling data is never working was fixed by Martin's pull
>>> back of topN patches, performance got GEOMEAN ~1% improvement(+24% for
>>> 511.povray_r specifically).
>>> Still, currently the default profile only generates SINGLE indirect target
>>> that called more than 75%.  This patch leverages MULTIPLE indirect
>>> targets use in LTO-WPA and LTO-LTRANS stage, as a result, function
>>> specialization, profiling, partial devirtualization, inlining and
>>> cloning could be done successfully based on it.
>>> Performance can get improved from 0.70 sec to 0.38 sec on simple tests.
>>> Details are:
>>>    1.  PGO with topn is enabled by default now, but only one indirect
>>>    target edge will be generated in ipa-profile pass, so add variables to enable
>>>    multiple speculative edges through passes, speculative_id will record the
>>>    direct edge index bind to the indirect edge, indirect_call_targets length
>>>    records how many direct edges owned by the indirect edge, postpone gimple_ic
>>>    to ipa-profile like default as inline pass will decide whether it is benefit
>>>    to transform indirect call.
>>>    2.  Use speculative_id to track and search the reference node matched
>>>    with the direct edge's callee for multiple targets.  Actually, it is the
>>>    caller's responsibility to handle the direct edges mapped to same indirect
>>>    edge.  speculative_call_info will return one of the direct edge specified,
>>>    this will leverage current IPA edge process framework mostly.
>>>    3.  Enable LTO WPA/LTRANS stage multiple indirect call targets analysis for
>>>    profile full support in ipa passes and cgraph_edge functions.  speculative_id
>>>    can be set by make_speculative id when multiple targets are binded to
>>>    one indirect edge, and cloned if new edge is cloned.  speculative_id
>>>    is streamed out and stream int by lto like lto_stmt_uid.
>>>    4.  Add 1 in module testcase and 2 cross module testcases.
>>>    5.  Bootstrap and regression test passed on Power8-LE.  No function
>>>    and performance regression for SPEC2017.
>>>
>>> gcc/ChangeLog
>>>
>>>     2019-09-25  Xiong Hu Luo  <luoxhu@linux.ibm.com>
>>>
>>>     PR ipa/69678
>>>     * cgraph.c (symbol_table::create_edge): Init speculative_id.
>>>     (cgraph_edge::make_speculative): Add param for setting speculative_id.
>>>     (cgraph_edge::speculative_call_info): Find reference by
>>>     speculative_id for multiple indirect targets.
>>>     (cgraph_edge::resolve_speculation): Decrease the speculations
>>>     for indirect edge, drop it's speculative if not direct target
>>>     left.
>>>     (cgraph_edge::redirect_call_stmt_to_callee): Likewise.
>>>     (cgraph_node::verify_node): Don't report error if speculative
>>>     edge not include statement.
>>>     (cgraph_edge::has_multiple_indirect_call_p): New function.
>>>     (cgraph_edge::has_indirect_call_p): New function.
>>>     * cgraph.h (struct indirect_target_info): New struct.
>>>     (indirect_call_targets): New vector variable.
>>>     (make_speculative): Add param for setting speculative_id.
>>>     (cgraph_edge::has_multiple_indirect_call_p): New declare.
>>>     (cgraph_edge::has_indirect_call_p): New declare.
>>>     (speculative_id): New variable.
>>>     * cgraphclones.c (cgraph_node::create_clone): Clone speculative_id.
>>>     * ipa-inline.c (inline_small_functions): Fix iterator update.
>>>     * ipa-profile.c (ipa_profile_generate_summary): Add indirect
>>>     multiple targets logic.
>>>     (ipa_profile): Likewise.
>>>     * ipa-ref.h (speculative_id): New variable.
>>>     * ipa.c (process_references): Fix typo.
>>>     * lto-cgraph.c (lto_output_edge): Add indirect multiple targets
>>>     logic.  Stream out speculative_id.
>>>     (input_edge): Likewise.
>>>     * predict.c (dump_prediction): Remove edges count assert to be
>>>     precise.
>>>     * symtab.c (symtab_node::create_reference): Init speculative_id.
>>>     (symtab_node::clone_references): Clone speculative_id.
>>>     (symtab_node::clone_referring): Clone speculative_id.
>>>     (symtab_node::clone_reference): Clone speculative_id.
>>>     (symtab_node::clear_stmts_in_references): Clear speculative_id.
>>>     * tree-inline.c (copy_bb): Duplicate all the speculative edges
>>>     if indirect call contains multiple speculative targets.
>>>     * tree-profile.c (gimple_gen_ic_profiler): Use the new variable
>>>     __gcov_indirect_call.counters and __gcov_indirect_call.callee.
>>>     (gimple_gen_ic_func_profiler): Likewise.
>>>     (pass_ipa_tree_profile::gate): Fix comment typos.
>>>     * value-prof.c  (gimple_ic_transform): Handle topn case.
>>>     Fix comment typos.
>>>
>>> gcc/testsuite/ChangeLog
>>>
>>>     2019-09-25  Xiong Hu Luo  <luoxhu@linux.ibm.com>
>>>
>>>     PR ipa/69678
>>>     * gcc.dg/tree-prof/indir-call-prof-topn.c: New testcase.
>>>     * gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c: New testcase.
>>>     * gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c: New testcase.
>>>     * gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c: New testcase.
>>> ---
>>>   gcc/cgraph.c                                  | 90 ++++++++++++++++-
>>>   gcc/cgraph.h                                  | 29 +++++-
>>>   gcc/cgraphclones.c                            |  1 +
>>>   gcc/ipa-inline.c                              | 15 +--
>>>   gcc/ipa-profile.c                             | 96 ++++++++++++++-----
>>>   gcc/ipa-ref.h                                 |  1 +
>>>   gcc/ipa.c                                     |  2 +-
>>>   gcc/lto-cgraph.c                              | 57 +++++++++--
>>>   gcc/predict.c                                 |  1 -
>>>   gcc/symtab.c                                  |  5 +
>>>   .../tree-prof/crossmodule-indir-call-topn-1.c | 35 +++++++
>>>   .../crossmodule-indir-call-topn-1a.c          | 22 +++++
>>>   .../tree-prof/crossmodule-indir-call-topn-2.c | 42 ++++++++
>>>   .../gcc.dg/tree-prof/indir-call-prof-topn.c   | 38 ++++++++
>>>   gcc/tree-inline.c                             | 19 ++++
>>>   gcc/tree-profile.c                            | 12 +--
>>>   gcc/value-prof.c                              | 86 +++++++++--------
>>>   17 files changed, 452 insertions(+), 99 deletions(-)
>>>   create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
>>>   create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
>>>   create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
>>>   create mode 100644 gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
>>>
>>> diff --git a/gcc/cgraph.c b/gcc/cgraph.c
>>> index 843891e9e56..9a28aca435f 100644
>>> --- a/gcc/cgraph.c
>>> +++ b/gcc/cgraph.c
>>> @@ -860,6 +860,7 @@ symbol_table::create_edge (cgraph_node *caller, cgraph_node *callee,
>>>     edge->prev_callee = NULL;
>>>     edge->next_callee = NULL;
>>>     edge->lto_stmt_uid = 0;
>>> +  edge->speculative_id = 0;
>>>       edge->count = count;
>>>   @@ -1051,7 +1052,8 @@ cgraph_edge::remove (void)
>>>      Return direct edge created.  */
>>>     cgraph_edge *
>>> -cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
>>> +cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count,
>>> +                   unsigned int speculative_id)
>>>   {
>>>     cgraph_node *n = caller;
>>>     ipa_ref *ref = NULL;
>>> @@ -1069,11 +1071,13 @@ cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
>>>     else
>>>       e2->can_throw_external = can_throw_external;
>>>     e2->lto_stmt_uid = lto_stmt_uid;
>>> +  e2->speculative_id = speculative_id;
>>>     e2->in_polymorphic_cdtor = in_polymorphic_cdtor;
>>>     count -= e2->count;
>>>     symtab->call_edge_duplication_hooks (this, e2);
>>>     ref = n->create_reference (n2, IPA_REF_ADDR, call_stmt);
>>>     ref->lto_stmt_uid = lto_stmt_uid;
>>> +  ref->speculative_id = speculative_id;
>>>     ref->speculative = speculative;
>>>     n2->mark_address_taken ();
>>>     return e2;
>>> @@ -1087,6 +1091,38 @@ cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
>>>      call) and if one of them exists, all of them must exist.
>>>        Given speculative call edge, return all three components.
>>> +
>>> +   For some indirect edge, it may maps to multiple direct edges, i.e.  1:N.
>>> +   check the speculative_id to return all the three components for specified
>>> +   direct edge or indirect edge.
>>> +   If input is indirect, caller of this function will get the direct edge one by
>>> +   one, get_edge will just return one of the direct edge mapped to the indirect
>>> +   edge, the returned direct edge will be resolved or redirected by the caller,
>>> +   then number of indirect calls (speculations) is deceased in each access.
>>> +   If input is direct, this function will get the indirect edge and reference
>>> +   with matched speculative_id, the returned edge will also be resolved or
>>> +   redirected, decrease the speculations accordingly.
>>> +   Speculations of indirect edge will be dropped only if all direct edges
>>> +   be handled.
>>> +
>>> +   e.g.  for indirect edge E statement "call call_dest":
>>> +
>>> +   Redirect N3 after redirected N2:
>>> +
>>> +   if (call_dest == N2)
>>> +     n2 ();
>>> +   else if (call_dest == N3)
>>> +     n3 ();
>>> +   else
>>> +     call call_dest
>>> +
>>> +   Resolve N3 and only redirect N2:
>>> +
>>> +   if (call_dest == N2)
>>> +     n2 ();
>>> +   else
>>> +     call call_dest
>>> +
>>>    */
>>>     void
>>> @@ -1126,7 +1162,7 @@ cgraph_edge::speculative_call_info (cgraph_edge *&direct,
>>>       reference = NULL;
>>>     for (i = 0; e->caller->iterate_reference (i, ref); i++)
>>> -    if (ref->speculative
>>> +    if (ref->speculative && ref->speculative_id == e->speculative_id
>>>       && ((ref->stmt && ref->stmt == e->call_stmt)
>>>           || (!ref->stmt && ref->lto_stmt_uid == e->lto_stmt_uid)))
>>>         {
>>> @@ -1187,7 +1223,21 @@ cgraph_edge::resolve_speculation (tree callee_decl)
>>>            in the functions inlined through it.  */
>>>       }
>>>     edge->count += e2->count;
>>> -  edge->speculative = false;
>>> +  /* edge is indirect, e2 is direct.  If edge contains multiple speculations,
>>> +     remove one of speculations for this indirect edge, then if edge still
>>> +     contains direct target, keep the speculation, next direct target
>>> +     will continue use it.  Give up speculation completely if no direct
>>> +     target is left for this indirect edge.  */
>>> +  if (edge->has_indirect_call_p ())
>>> +    {
>>> +      /* As the direct targets are sorted by decrease, delete the first target
>>> +     when it is resolved.  */
>>> +      edge->indirect_info->indirect_call_targets->ordered_remove (0);
>>> +      if (!edge->indirect_info->indirect_call_targets->length ())
>>> +    edge->speculative = false;
>>> +    }
>>> +  else
>>> +    edge->speculative = false;
>>>     e2->speculative = false;
>>>     ref->remove_reference ();
>>>     if (e2->indirect_unknown_callee || e2->inline_failed)
>>> @@ -1321,7 +1371,21 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
>>>         e->caller->set_call_stmt_including_clones (e->call_stmt, new_stmt,
>>>                                false);
>>>         e->count = gimple_bb (e->call_stmt)->count;
>>> -      e2->speculative = false;
>>> +      /* edge is direct, e2 is indirect here.  If e2 contains multiple
>>> +         speculations, remove one of speculations for this indirect edge,
>>> +         then if e2 still contains direct target, keep the speculation,
>>> +         next direct target will continue use it.  Give up speculation
>>> +         completely if no direct target is left for this indirect e2.  */
>>> +      if (e2->has_indirect_call_p ())
>>> +        {
>>> +          /* As the direct targets are sorted by decrease, delete the first
>>> +         target when it is redirected.  */
>>> +          e2->indirect_info->indirect_call_targets->ordered_remove (0);
>>> +          if (!e2->indirect_info->indirect_call_targets->length ())
>>> +        e2->speculative = false;
>>> +        }
>>> +      else
>>> +        e2->speculative = false;
>>>         e2->count = gimple_bb (e2->call_stmt)->count;
>>>         ref->speculative = false;
>>>         ref->stmt = NULL;
>>> @@ -3445,7 +3509,7 @@ cgraph_node::verify_node (void)
>>>           for (e = callees; e; e = e->next_callee)
>>>       {
>>> -      if (!e->aux)
>>> +      if (!e->aux && !e->speculative)
>>>           {
>>>             error ("edge %s->%s has no corresponding call_stmt",
>>>                identifier_to_locale (e->caller->name ()),
>>> @@ -3872,6 +3936,22 @@ cgraph_edge::possibly_call_in_translation_unit_p (void)
>>>     return node->get_availability () >= AVAIL_AVAILABLE;
>>>   }
>>>   +/* Return true if this edge has multiple indirect call targets.  */
>>> +bool
>>> +cgraph_edge::has_multiple_indirect_call_p (void)
>>> +{
>>> +  return indirect_info && indirect_info->indirect_call_targets
>>> +     && indirect_info->indirect_call_targets->length () > 1;
>>> +}
>>
>> For multiline && expression, we typically wrap the whole condition
>> in '(' and ')'.
>>
>>> +
>>> +/* Return true if this edge has at least one indirect call target.  */
>>> +bool
>>> +cgraph_edge::has_indirect_call_p (void)
>>> +{
>>> +  return indirect_info && indirect_info->indirect_call_targets
>>> +     && indirect_info->indirect_call_targets->length ();
>>> +}
>>
>> Likewise here.
>>
>>> +
>>>   /* A stashed copy of "symtab" for use by selftest::symbol_table_test.
>>>      This needs to be a global so that it can be a GC root, and thus
>>>      prevent the stashed copy from being garbage-collected if the GC runs
>>> diff --git a/gcc/cgraph.h b/gcc/cgraph.h
>>> index 4c54210123a..33c8454c4e0 100644
>>> --- a/gcc/cgraph.h
>>> +++ b/gcc/cgraph.h
>>> @@ -1636,6 +1636,16 @@ private:
>>>     void make_speculative (tree otr_type = NULL);
>>>   };
>>>   +/* Structure containing indirect target information from profile.  */
>>> +
>>> +struct GTY (()) indirect_target_info
>>> +{
>>> +  /* Profile_id of common target obtained from profile.  */
>>> +  unsigned int common_target_id;
>>> +  /* Probability that call will land in function with COMMON_TARGET_ID.  */
>>> +  int common_target_probability;
>>> +};
>>> +
>>>   /* Structure containing additional information about an indirect call.  */
>>>     class GTY(()) cgraph_indirect_call_info
>>> @@ -1654,10 +1664,9 @@ public:
>>>     int param_index;
>>>     /* ECF flags determined from the caller.  */
>>>     int ecf_flags;
>>> -  /* Profile_id of common target obtrained from profile.  */
>>> -  int common_target_id;
>>> -  /* Probability that call will land in function with COMMON_TARGET_ID.  */
>>> -  int common_target_probability;
>>> +
>>> +  /* An indirect call may contain one or multiple call targets.  */
>>> +  vec<indirect_target_info, va_gc> *indirect_call_targets;
>>>       /* Set when the call is a virtual call with the parameter being the
>>>        associated object pointer rather than a simple direct call.  */
>>> @@ -1714,7 +1723,8 @@ public:
>>>     /* Turn edge into speculative call calling N2. Update
>>>        the profile so the direct call is taken COUNT times
>>>        with FREQUENCY.  */
>>> -  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count);
>>> +  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count,
>>> +                 unsigned int speculative_id = 0);
>>>        /* Given speculative call edge, return all three components.  */
>>>     void speculative_call_info (cgraph_edge *&direct, cgraph_edge *&indirect,
>>> @@ -1773,6 +1783,12 @@ public:
>>>        be internal to the current translation unit.  */
>>>     bool possibly_call_in_translation_unit_p (void);
>>>   +  /* Return true if this edge has multiple indirect call targets.  */
>>> +  bool has_multiple_indirect_call_p (void);
>>> +
>>> +  /* Return true if this edge has at least one indirect call target.  */
>>> +  bool has_indirect_call_p (void);
>>> +
>>>     /* Expected number of executions: calculated in profile.c.  */
>>>     profile_count count;
>>>     cgraph_node *caller;
>>> @@ -1792,6 +1808,9 @@ public:
>>>     /* The stmt_uid of call_stmt.  This is used by LTO to recover the call_stmt
>>>        when the function is serialized in.  */
>>>     unsigned int lto_stmt_uid;
>>> +  /* speculative id is used by multiple indirect targets when the function is
>>> +   speculated.  */
>>> +  unsigned int speculative_id;
>>>     /* Whether this edge was made direct by indirect inlining.  */
>>>     unsigned int indirect_inlining_edge : 1;
>>>     /* Whether this edge describes an indirect call with an undetermined
>>> diff --git a/gcc/cgraphclones.c b/gcc/cgraphclones.c
>>> index fa753697c78..5dbd8d90b77 100644
>>> --- a/gcc/cgraphclones.c
>>> +++ b/gcc/cgraphclones.c
>>> @@ -128,6 +128,7 @@ cgraph_edge::clone (cgraph_node *n, gcall *call_stmt, unsigned stmt_uid,
>>>     new_edge->inline_failed = inline_failed;
>>>     new_edge->indirect_inlining_edge = indirect_inlining_edge;
>>>     new_edge->lto_stmt_uid = stmt_uid;
>>> +  new_edge->speculative_id = speculative_id;
>>>     /* Clone flags that depend on call_stmt availability manually.  */
>>>     new_edge->can_throw_external = can_throw_external;
>>>     new_edge->call_stmt_cannot_inline_p = call_stmt_cannot_inline_p;
>>> diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
>>> index b62d280eb25..6136214f9ac 100644
>>> --- a/gcc/ipa-inline.c
>>> +++ b/gcc/ipa-inline.c
>>> @@ -1881,12 +1881,15 @@ inline_small_functions (void)
>>>       }
>>>         if (has_speculative)
>>>       for (edge = node->callees; edge; edge = next)
>>> -      if (edge->speculative && !speculation_useful_p (edge,
>>> -                              edge->aux != NULL))
>>> -        {
>>> -          edge->resolve_speculation ();
>>> -          update = true;
>>> -        }
>>> +      {
>>> +        next = edge->next_callee;
>>> +        if (edge->speculative
>>> +        && !speculation_useful_p (edge, edge->aux != NULL))
>>> +          {
>>> +        edge->resolve_speculation ();
>>> +        update = true;
>>> +          }
>>> +      }
>>>         if (update)
>>>       {
>>>         struct cgraph_node *where = node->global.inlined_to
>>> diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
>>> index 970dba39c80..342e8ea05d1 100644
>>> --- a/gcc/ipa-profile.c
>>> +++ b/gcc/ipa-profile.c
>>> @@ -192,23 +192,35 @@ ipa_profile_generate_summary (void)
>>>             if (h)
>>>               {
>>>                 gcov_type val, count, all;
>>> -              if (get_nth_most_common_value (NULL, "indirect call", h,
>>> -                             &val, &count, &all))
>>> +              struct cgraph_edge *e = node->get_edge (stmt);
>>> +              if (e && !e->indirect_unknown_callee)
>>> +            continue;
>>> +
>>> +              struct indirect_target_info item;
>>> +              for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
>>>               {
>>> -              struct cgraph_edge * e = node->get_edge (stmt);
>>> -              if (e && !e->indirect_unknown_callee)
>>> +              if (!get_nth_most_common_value (NULL, "indirect call",
>>> +                              h, &val, &count, &all,
>>> +                              j))
>>> +                continue;
>>> +
>>> +              if (val == 0)
>>>                   continue;
>>>   -              e->indirect_info->common_target_id = val;
>>> -              e->indirect_info->common_target_probability
>>> +              item.common_target_id = val;
>>> +              item.common_target_probability
>>>                   = GCOV_COMPUTE_SCALE (count, all);
>>
>> There's one of the places where you can use the constructor.
>>
>>> -              if (e->indirect_info->common_target_probability > REG_BR_PROB_BASE)
>>> +              if (item.common_target_probability > REG_BR_PROB_BASE)
>>>                   {
>>>                     if (dump_file)
>>> -                fprintf (dump_file, "Probability capped to 1\n");
>>> -                  e->indirect_info->common_target_probability = REG_BR_PROB_BASE;
>>> +                fprintf (dump_file,
>>> +                     "Probability capped to 1\n");
>>> +                  item.common_target_probability = REG_BR_PROB_BASE;
>>>                   }
>>> +              vec_safe_push (
>>> +                e->indirect_info->indirect_call_targets, item);
>>>               }
>>> +
>>>                 gimple_remove_histogram_value (DECL_STRUCT_FUNCTION (node->decl),
>>>                                 stmt, h);
>>>               }
>>> @@ -492,6 +504,7 @@ ipa_profile (void)
>>>     int nindirect = 0, ncommon = 0, nunknown = 0, nuseless = 0, nconverted = 0;
>>>     int nmismatch = 0, nimpossible = 0;
>>>     bool node_map_initialized = false;
>>> +  gcov_type threshold;
>>>       if (dump_file)
>>>       dump_histogram (dump_file, histogram);
>>> @@ -500,14 +513,12 @@ ipa_profile (void)
>>>         overall_time += histogram[i]->count * histogram[i]->time;
>>>         overall_size += histogram[i]->size;
>>>       }
>>> +  threshold = 0;
>>>     if (overall_time)
>>>       {
>>> -      gcov_type threshold;
>>> -
>>>         gcc_assert (overall_size);
>>>           cutoff = (overall_time * PARAM_VALUE (HOT_BB_COUNT_WS_PERMILLE) + 500) / 1000;
>>> -      threshold = 0;
>>>         for (i = 0; cumulated < cutoff; i++)
>>>       {
>>>         cumulated += histogram[i]->count * histogram[i]->time;
>>> @@ -543,7 +554,7 @@ ipa_profile (void)
>>>     histogram.release ();
>>>     histogram_pool.release ();
>>>   -  /* Produce speculative calls: we saved common traget from porfiling into
>>> +  /* Produce speculative calls: we saved common target from profiling into
>>>        e->common_target_id.  Now, at link time, we can look up corresponding
>>>        function node and produce speculative call.  */
>>>   @@ -558,13 +569,37 @@ ipa_profile (void)
>>>       {
>>>         if (n->count.initialized_p ())
>>>           nindirect++;
>>> -      if (e->indirect_info->common_target_id)
>>> +      if (e->has_indirect_call_p ())
>>>           {
>>>             if (!node_map_initialized)
>>> -            init_node_map (false);
>>> +        init_node_map (false);
>>>             node_map_initialized = true;
>>>             ncommon++;
>>> -          n2 = find_func_by_profile_id (e->indirect_info->common_target_id);
>>> +
>>> +          if (in_lto_p)
>>> +        {
>>> +          if (dump_file)
>>> +            {
>>> +              fprintf (dump_file,
>>> +                   "Updating hotness threshold in LTO mode.\n");
>>> +              fprintf (dump_file, "Updated min count: %" PRId64 "\n",
>>> +                   (int64_t) threshold);
>>> +            }
>>> +          set_hot_bb_threshold (threshold
>>> +            / e->indirect_info->indirect_call_targets->length ());
>>> +        }
>>> +
>>> +          unsigned speculative_id = 0;
>>> +          struct indirect_target_info *item;
>>> +          /* The code below is not formatted yet for review convenience.
>>> +         Move to a seprate small function is not easy as too many local
>>> +         variables used in it.  Need format and remove this comments
>>> +         once got approved.  */
>>> +          FOR_EACH_VEC_SAFE_ELT (e->indirect_info->indirect_call_targets, i,
>>> +                     item)
>>> +           {
>>> +          bool speculative_found = false;
>>> +          n2 = find_func_by_profile_id (item->common_target_id);
>>>             if (n2)
>>>           {
>>>             if (dump_file)
>>> @@ -573,11 +608,10 @@ ipa_profile (void)
>>>                      " other module %s => %s, prob %3.2f\n",
>>>                      n->dump_name (),
>>>                      n2->dump_name (),
>>> -                   e->indirect_info->common_target_probability
>>> -                   / (float)REG_BR_PROB_BASE);
>>> +                   item->common_target_probability
>>> +                 / (float) REG_BR_PROB_BASE);
>>>               }
>>> -          if (e->indirect_info->common_target_probability
>>> -              < REG_BR_PROB_BASE / 2)
>>> +          if (item->common_target_probability < REG_BR_PROB_BASE / 2)
>>>               {
>>>                 nuseless++;
>>>                 if (dump_file)
>>> @@ -613,7 +647,7 @@ ipa_profile (void)
>>>                 if (dump_file)
>>>               fprintf (dump_file,
>>>                    "Not speculating: "
>>> -                 "parameter count mistmatch\n");
>>> +                 "parameter count mismatch\n");
>>>               }
>>>             else if (e->indirect_info->polymorphic
>>>                  && !opt_for_fn (n->decl, flag_devirtualize)
>>> @@ -640,20 +674,30 @@ ipa_profile (void)
>>>                   n2 = alias;
>>>               }
>>>                 nconverted++;
>>> -              e->make_speculative
>>> -            (n2,
>>> -             e->count.apply_probability
>>> -                     (e->indirect_info->common_target_probability));
>>> +              e->make_speculative (n2,
>>> +                       e->count.apply_probability (
>>> +                         item->common_target_probability),
>>> +                       speculative_id);
>>>                 update = true;
>>> +              speculative_id++;
>>> +              speculative_found = true;
>>>               }
>>>           }
>>>             else
>>>           {
>>>             if (dump_file)
>>>               fprintf (dump_file, "Function with profile-id %i not found.\n",
>>> -                 e->indirect_info->common_target_id);
>>> +                 item->common_target_id);
>>>             nunknown++;
>>>           }
>>> +          if (!speculative_found)
>>> +        {
>>> +          /* Remove item from indirect_call_targets if no
>>> +             speculative edge generated, rollback the iteration.  */
>>> +          e->indirect_info->indirect_call_targets->ordered_remove (i);
>>> +          i--;
>>> +        }
>>> +           }
>>>           }
>>>        }
>>>          if (update)
>>> diff --git a/gcc/ipa-ref.h b/gcc/ipa-ref.h
>>> index 0d8e509c932..3e6562ec9d1 100644
>>> --- a/gcc/ipa-ref.h
>>> +++ b/gcc/ipa-ref.h
>>> @@ -59,6 +59,7 @@ public:
>>>     symtab_node *referred;
>>>     gimple *stmt;
>>>     unsigned int lto_stmt_uid;
>>> +  unsigned int speculative_id;
>>>     unsigned int referred_index;
>>>     ENUM_BITFIELD (ipa_ref_use) use:3;
>>>     unsigned int speculative:1;
>>> diff --git a/gcc/ipa.c b/gcc/ipa.c
>>> index 6b84e1f9bda..a10b0603f14 100644
>>> --- a/gcc/ipa.c
>>> +++ b/gcc/ipa.c
>>> @@ -166,7 +166,7 @@ process_references (symtab_node *snode,
>>>      devirtualization happens.  After inlining still keep their declarations
>>>      around, so we can devirtualize to a direct call.
>>>   -   Also try to make trivial devirutalization when no or only one target is
>>> +   Also try to make trivial devirtualization when no or only one target is
>>>      possible.  */
>>>     static void
>>> diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
>>> index bc0f0107333..61380dcc7b8 100644
>>> --- a/gcc/lto-cgraph.c
>>> +++ b/gcc/lto-cgraph.c
>>> @@ -238,6 +238,7 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
>>>     unsigned int uid;
>>>     intptr_t ref;
>>>     struct bitpack_d bp;
>>> +  unsigned len;
>>>       if (edge->indirect_unknown_callee)
>>>       streamer_write_enum (ob->main_stream, LTO_symtab_tags, LTO_symtab_last_tag,
>>> @@ -265,6 +266,7 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
>>>     bp_pack_enum (&bp, cgraph_inline_failed_t,
>>>               CIF_N_REASONS, edge->inline_failed);
>>>     bp_pack_var_len_unsigned (&bp, uid);
>>> +  bp_pack_var_len_unsigned (&bp, edge->speculative_id);
>>>     bp_pack_value (&bp, edge->indirect_inlining_edge, 1);
>>>     bp_pack_value (&bp, edge->speculative, 1);
>>>     bp_pack_value (&bp, edge->call_stmt_cannot_inline_p, 1);
>>> @@ -291,11 +293,27 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
>>>     streamer_write_bitpack (&bp);
>>>     if (edge->indirect_unknown_callee)
>>>       {
>>> -      streamer_write_hwi_stream (ob->main_stream,
>>> -                     edge->indirect_info->common_target_id);
>>> -      if (edge->indirect_info->common_target_id)
>>> -    streamer_write_hwi_stream
>>> -       (ob->main_stream, edge->indirect_info->common_target_probability);
>>> +      struct indirect_target_info *item;
>>> +      unsigned int i;
>>> +      len = edge->has_indirect_call_p ()
>>> +          ? edge->indirect_info->indirect_call_targets->length ()
>>> +          : 0;
>>> +      gcc_assert (len <= GCOV_TOPN_VALUES);
>>> +
>>> +      streamer_write_hwi_stream (ob->main_stream, len);
>>> +
>>> +      if (len)
>>> +    {
>>> +      FOR_EACH_VEC_SAFE_ELT (edge->indirect_info->indirect_call_targets, i,
>>> +                 item)
>>> +        {
>>> +          streamer_write_hwi_stream (ob->main_stream,
>>> +                     item->common_target_id);
>>> +          if (item->common_target_id)
>>> +        streamer_write_hwi_stream (ob->main_stream,
>>> +                       item->common_target_probability);
>>> +        }
>>> +    }
>>>       }
>>>   }
>>>   @@ -688,6 +706,7 @@ lto_output_ref (struct lto_simple_output_block *ob, struct ipa_ref *ref,
>>>         if (ref->stmt)
>>>       uid = gimple_uid (ref->stmt) + 1;
>>>         streamer_write_hwi_stream (ob->main_stream, uid);
>>> +      streamer_write_hwi_stream (ob->main_stream, ref->speculative_id);
>>>       }
>>>   }
>>>   @@ -1419,7 +1438,10 @@ input_ref (class lto_input_block *ib,
>>>     ref = referring_node->create_reference (node, use);
>>>     ref->speculative = speculative;
>>>     if (is_a <cgraph_node *> (referring_node))
>>> -    ref->lto_stmt_uid = streamer_read_hwi (ib);
>>> +    {
>>> +      ref->lto_stmt_uid = streamer_read_hwi (ib);
>>> +      ref->speculative_id = streamer_read_hwi (ib);
>>> +    }
>>>   }
>>>     /* Read an edge from IB.  NODES points to a vector of previously read nodes for
>>> @@ -1433,11 +1455,12 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
>>>   {
>>>     struct cgraph_node *caller, *callee;
>>>     struct cgraph_edge *edge;
>>> -  unsigned int stmt_id;
>>> +  unsigned int stmt_id, speculative_id;
>>>     profile_count count;
>>>     cgraph_inline_failed_t inline_failed;
>>>     struct bitpack_d bp;
>>>     int ecf_flags = 0;
>>> +  unsigned i, len;
>>>       caller = dyn_cast<cgraph_node *> (nodes[streamer_read_hwi (ib)]);
>>>     if (caller == NULL || caller->decl == NULL_TREE)
>>> @@ -1457,6 +1480,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
>>>     bp = streamer_read_bitpack (ib);
>>>     inline_failed = bp_unpack_enum (&bp, cgraph_inline_failed_t, CIF_N_REASONS);
>>>     stmt_id = bp_unpack_var_len_unsigned (&bp);
>>> +  speculative_id = bp_unpack_var_len_unsigned (&bp);
>>>       if (indirect)
>>>       edge = caller->create_indirect_edge (NULL, 0, count);
>>> @@ -1466,6 +1490,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
>>>     edge->indirect_inlining_edge = bp_unpack_value (&bp, 1);
>>>     edge->speculative = bp_unpack_value (&bp, 1);
>>>     edge->lto_stmt_uid = stmt_id;
>>> +  edge->speculative_id = speculative_id;
>>>     edge->inline_failed = inline_failed;
>>>     edge->call_stmt_cannot_inline_p = bp_unpack_value (&bp, 1);
>>>     edge->can_throw_external = bp_unpack_value (&bp, 1);
>>> @@ -1485,9 +1510,21 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
>>>         if (bp_unpack_value (&bp, 1))
>>>       ecf_flags |= ECF_RETURNS_TWICE;
>>>         edge->indirect_info->ecf_flags = ecf_flags;
>>> -      edge->indirect_info->common_target_id = streamer_read_hwi (ib);
>>> -      if (edge->indirect_info->common_target_id)
>>> -        edge->indirect_info->common_target_probability = streamer_read_hwi (ib);
>>> +
>>> +      len = streamer_read_hwi (ib);
>>> +
>>> +      gcc_assert (len <= GCOV_TOPN_VALUES);
>>> +
>>> +      if (len)
>>> +    {
>>> +      indirect_target_info item;
>>> +      for (i = 0; i < len; i++)
>>> +        {
>>> +          item.common_target_id = streamer_read_hwi (ib);
>>> +          item.common_target_probability = streamer_read_hwi (ib);
>>> +          vec_safe_push (edge->indirect_info->indirect_call_targets, item);
>>> +        }
>>> +    }
>>>       }
>>>   }
>>>   diff --git a/gcc/predict.c b/gcc/predict.c
>>> index 915f0806b11..3f56fa3a74a 100644
>>> --- a/gcc/predict.c
>>> +++ b/gcc/predict.c
>>> @@ -762,7 +762,6 @@ dump_prediction (FILE *file, enum br_predictor predictor, int probability,
>>>         && bb->count.precise_p ()
>>>         && reason == REASON_NONE)
>>>       {
>>> -      gcc_assert (e->count ().precise_p ());
>>>         fprintf (file, ";;heuristics;%s;%" PRId64 ";%" PRId64 ";%.1f;\n",
>>>              predictor_info[predictor].name,
>>>              bb->count.to_gcov_type (), e->count ().to_gcov_type (),
>>> diff --git a/gcc/symtab.c b/gcc/symtab.c
>>> index ee9723c3453..d4c36fd3e5a 100644
>>> --- a/gcc/symtab.c
>>> +++ b/gcc/symtab.c
>>> @@ -603,6 +603,7 @@ symtab_node::create_reference (symtab_node *referred_node,
>>>     ref->referred = referred_node;
>>>     ref->stmt = stmt;
>>>     ref->lto_stmt_uid = 0;
>>> +  ref->speculative_id = 0;
>>>     ref->use = use_type;
>>>     ref->speculative = 0;
>>>   @@ -660,6 +661,7 @@ symtab_node::clone_references (symtab_node *node)
>>>         ref2 = create_reference (ref->referred, ref->use, ref->stmt);
>>>         ref2->speculative = speculative;
>>>         ref2->lto_stmt_uid = stmt_uid;
>>> +      ref2->speculative_id = ref->speculative_id;
>>>       }
>>>   }
>>>   @@ -678,6 +680,7 @@ symtab_node::clone_referring (symtab_node *node)
>>>         ref2 = ref->referring->create_reference (this, ref->use, ref->stmt);
>>>         ref2->speculative = speculative;
>>>         ref2->lto_stmt_uid = stmt_uid;
>>> +      ref2->speculative_id = ref->speculative_id;
>>>       }
>>>   }
>>>   @@ -693,6 +696,7 @@ symtab_node::clone_reference (ipa_ref *ref, gimple *stmt)
>>>     ref2 = create_reference (ref->referred, ref->use, stmt);
>>>     ref2->speculative = speculative;
>>>     ref2->lto_stmt_uid = stmt_uid;
>>> +  ref2->speculative_id = ref->speculative_id;
>>>     return ref2;
>>>   }
>>>   @@ -747,6 +751,7 @@ symtab_node::clear_stmts_in_references (void)
>>>         {
>>>       r->stmt = NULL;
>>>       r->lto_stmt_uid = 0;
>>> +    r->speculative_id = 0;
>>>         }
>>>   }
>>>   diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
>>> new file mode 100644
>>> index 00000000000..e0a83c2e067
>>> --- /dev/null
>>> +++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
>>> @@ -0,0 +1,35 @@
>>> +/* { dg-require-effective-target lto } */
>>> +/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
>>> +/* { dg-require-profiling "-fprofile-generate" } */
>>> +/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate --param indir-call-topn-profile=1" } */
>>> +
>>> +#include <stdio.h>
>>> +
>>> +typedef int (*fptr) (int);
>>> +int
>>> +one (int a);
>>> +
>>> +int
>>> +two (int a);
>>> +
>>> +fptr table[] = {&one, &two};
>>> +
>>> +int
>>> +main()
>>> +{
>>> +  int i, x;
>>> +  fptr p = &one;
>>> +
>>> +  x = one (3);
>>> +
>>> +  for (i = 0; i < 350000000; i++)
>>> +    {
>>> +      x = (*p) (3);
>>> +      p = table[x];
>>> +    }
>>> +  printf ("done:%d\n", x);
>>> +}
>>> +
>>> +/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* one transformation on insn" "profile_estimate" } } */
>>> +/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* two transformation on insn" "profile_estimate" } } */
>>> +
>>> diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
>>> new file mode 100644
>>> index 00000000000..a8c6e365fb9
>>> --- /dev/null
>>> +++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
>>> @@ -0,0 +1,22 @@
>>> +/* It seems there is no way to avoid the other source of mulitple
>>> +   source testcase from being compiled independently.  Just avoid
>>> +   error.  */
>>> +#ifdef DOJOB
>>> +int
>>> +one (int a)
>>> +{
>>> +  return 1;
>>> +}
>>> +
>>> +int
>>> +two (int a)
>>> +{
>>> +  return 0;
>>> +}
>>> +#else
>>> +int
>>> +main()
>>> +{
>>> +  return 0;
>>> +}
>>> +#endif
>>> diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
>>> new file mode 100644
>>> index 00000000000..aa3887fde83
>>> --- /dev/null
>>> +++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
>>> @@ -0,0 +1,42 @@
>>> +/* { dg-require-effective-target lto } */
>>> +/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
>>> +/* { dg-require-profiling "-fprofile-generate" } */
>>> +/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate --param indir-call-topn-profile=1" } */
>>> +
>>> +#include <stdio.h>
>>> +
>>> +typedef int (*fptr) (int);
>>> +int
>>> +one (int a);
>>> +
>>> +int
>>> +two (int a);
>>> +
>>> +fptr table[] = {&one, &two};
>>> +
>>> +int foo ()
>>> +{
>>> +  int i, x;
>>> +  fptr p = &one;
>>> +
>>> +  x = one (3);
>>> +
>>> +  for (i = 0; i < 350000000; i++)
>>> +    {
>>> +      x = (*p) (3);
>>> +      p = table[x];
>>> +    }
>>> +  return x;
>>> +}
>>> +
>>> +int
>>> +main()
>>> +{
>>> +  int x = foo ();
>>> +  printf ("done:%d\n", x);
>>> +}
>>> +
>>> +/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* one transformation on insn" "profile_estimate" } } */
>>> +/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* two transformation on insn" "profile_estimate" } } */
>>> +
>>> +
>>> diff --git a/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
>>> new file mode 100644
>>> index 00000000000..951bc7ddd19
>>> --- /dev/null
>>> +++ b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
>>> @@ -0,0 +1,38 @@
>>> +/* { dg-require-profiling "-fprofile-generate" } */
>>> +/* { dg-options "-O2 -fdump-ipa-profile --param indir-call-topn-profile=1" } */
>>> +
>>> +#include <stdio.h>
>>> +
>>> +typedef int (*fptr) (int);
>>> +int
>>> +one (int a)
>>> +{
>>> +  return 1;
>>> +}
>>> +
>>> +int
>>> +two (int a)
>>> +{
>>> +  return 0;
>>> +}
>>> +
>>> +fptr table[] = {&one, &two};
>>> +
>>> +int
>>> +main()
>>> +{
>>> +  int i, x;
>>> +  fptr p = &one;
>>> +
>>> +  one (3);
>>> +
>>> +  for (i = 0; i < 350000000; i++)
>>> +    {
>>> +      x = (*p) (3);
>>> +      p = table[x];
>>> +    }
>>> +  printf ("done:%d\n", x);
>>> +}
>>> +
>>> +/* { dg-final-use-not-autofdo { scan-ipa-dump "Indirect call -> direct call.* one transformation on insn" "profile" } } */
>>> +/* { dg-final-use-not-autofdo { scan-ipa-dump "Indirect call -> direct call.* two transformation on insn" "profile" } } */
>>> diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
>>> index b9c1a3b1456..fe3e172fbd1 100644
>>> --- a/gcc/tree-inline.c
>>> +++ b/gcc/tree-inline.c
>>> @@ -2167,6 +2167,25 @@ copy_bb (copy_body_data *id, basic_block bb,
>>>                   gcc_assert (!edge->indirect_unknown_callee);
>>>                 old_edge->speculative_call_info (direct, indirect, ref);
>>> +              while (old_edge->next_callee
>>> +                 && old_edge->next_callee->speculative
>>> +                 && indirect->has_multiple_indirect_call_p ())
>>> +                {
>>> +                  /* Some speculative calls may contain more than
>>> +                 one direct target, loop iterate it to clone all
>>> +                 related direct edges before cloning the related
>>> +                 indirect edge.  */
>>> +                  id->dst_node->clone_reference (ref, stmt);
>>> +
>>> +                  edge = old_edge->next_callee;
>>> +                  edge = edge->clone (id->dst_node, call_stmt,
>>> +                          gimple_uid (stmt), num, den,
>>> +                          true);
>>> +                  old_edge = old_edge->next_callee;
>>> +                  gcc_assert (!edge->indirect_unknown_callee);
>>> +                  old_edge->speculative_call_info (direct, indirect,
>>> +                                   ref);
>>> +                }
>>>                   profile_count indir_cnt = indirect->count;
>>>                 indirect = indirect->clone (id->dst_node, call_stmt,
>>> diff --git a/gcc/tree-profile.c b/gcc/tree-profile.c
>>> index 4c1ead5781f..ef7748668f8 100644
>>> --- a/gcc/tree-profile.c
>>> +++ b/gcc/tree-profile.c
>>> @@ -74,8 +74,8 @@ static GTY(()) tree ic_tuple_callee_field;
>>>   /* Do initialization work for the edge profiler.  */
>>>     /* Add code:
>>> -   __thread gcov*    __gcov_indirect_call_counters; // pointer to actual counter
>>> -   __thread void*    __gcov_indirect_call_callee; // actual callee address
>>> +   __thread gcov*    __gcov_indirect_call.counters; // pointer to actual counter
>>> +   __thread void*    __gcov_indirect_call.callee; // actual callee address
>>>      __thread int __gcov_function_counter; // time profiler function counter
>>>   */
>>>   static void
>>> @@ -382,7 +382,7 @@ gimple_gen_ic_profiler (histogram_value value, unsigned tag)
>>>         f_1 = foo;
>>>         __gcov_indirect_call.counters = &__gcov4.main[0];
>>>         PROF_9 = f_1;
>>> -      __gcov_indirect_call_callee = PROF_9;
>>> +      __gcov_indirect_call.callee = PROF_9;
>>>         _4 = f_1 ();
>>>      */
>>>   @@ -445,11 +445,11 @@ gimple_gen_ic_func_profiler (void)
>>>       /* Insert code:
>>>   -     if (__gcov_indirect_call_callee != NULL)
>>> +     if (__gcov_indirect_call.callee != NULL)
>>>          __gcov_indirect_call_profiler_v3 (profile_id, &current_function_decl);
>>>          The function __gcov_indirect_call_profiler_v3 is responsible for
>>> -     resetting __gcov_indirect_call_callee to NULL.  */
>>> +     resetting __gcov_indirect_call.callee to NULL.  */
>>>       gimple_stmt_iterator gsi = gsi_start_bb (cond_bb);
>>>     void0 = build_int_cst (ptr_type_node, 0);
>>> @@ -891,7 +891,7 @@ pass_ipa_tree_profile::gate (function *)
>>>   {
>>>     /* When profile instrumentation, use or test coverage shall be performed.
>>>        But for AutoFDO, this there is no instrumentation, thus this pass is
>>> -     diabled.  */
>>> +     disabled.  */
>>>     return (!in_lto_p && !flag_auto_profile
>>>         && (flag_branch_probabilities || flag_test_coverage
>>>             || profile_arc_flag));
>>> diff --git a/gcc/value-prof.c b/gcc/value-prof.c
>>> index 55ea0973a03..0588df0fce9 100644
>>> --- a/gcc/value-prof.c
>>> +++ b/gcc/value-prof.c
>>> @@ -1406,11 +1406,10 @@ gimple_ic (gcall *icall_stmt, struct cgraph_node *direct_call,
>>>     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:
>>> - */
>>> +/* There maybe multiple indirect targets in histogram.  Check every
>>> +   indirect/virtual call if callee function exists, if not exist, leave it to
>>> +   LTO stage for later process.  Modify code of this indirect call to an if-else
>>> +   structure in ipa-profile finally.  */
>>>     static bool
>>>   gimple_ic_transform (gimple_stmt_iterator *gsi)
>>> @@ -1434,48 +1433,57 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
>>>     if (!histogram)
>>>       return false;
>>
>> The function is not correct, note that the function can now return true
>> when this transformation happens:
>>           "Indirect call -> direct call from other "
>>           "module %T=> %i (will resolve only with LTO)\n",
>>
>> Current trunk returns false in that case.
>>
>>>   -  if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
>>> -                  &count, &all))
>>> -    return false;
>>> +  count = 0;
>>> +  all = histogram->hvalue.counters[0];
>>>   -  if (4 * count <= 3 * all)
>>> -    return false;
>>> +  for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
>>> +    {
>>> +      if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
>>> +                      &count, &all, j))
>>> +    continue;
>>
>> You should break here as get_nth_most_common_value (..., j + 1) will also return
>> false.
>>
>>>   -  direct_call = find_func_by_profile_id ((int)val);
>>> +      /* Minimum probability.  should be higher than 25%.  */
>>> +      if (4 * count <= all)
>>> +    continue;
>>
>> You can break here as well.
>>
>> Thank you,
>> Martin
>>
>>>   -  if (direct_call == NULL)
>>> -    {
>>> -      if (val)
>>> +      direct_call = find_func_by_profile_id ((int) val);
>>> +
>>> +      if (direct_call == NULL)
>>> +    {
>>> +      if (val)
>>> +        {
>>> +          if (dump_enabled_p ())
>>> +        dump_printf_loc (
>>> +          MSG_MISSED_OPTIMIZATION, stmt,
>>> +          "Indirect call -> direct call from other "
>>> +          "module %T=> %i (will resolve only with LTO)\n",
>>> +          gimple_call_fn (stmt), (int) val);
>>> +        }
>>> +      continue;
>>> +    }
>>> +
>>> +      if (!check_ic_target (stmt, direct_call))
>>>       {
>>>         if (dump_enabled_p ())
>>> -        dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
>>> -                 "Indirect call -> direct call from other "
>>> -                 "module %T=> %i (will resolve only with LTO)\n",
>>> -                 gimple_call_fn (stmt), (int)val);
>>> +        dump_printf_loc (
>>> +          MSG_MISSED_OPTIMIZATION, stmt,
>>> +          "Indirect call -> direct call %T => %T "
>>> +          "transformation skipped because of type mismatch: %G",
>>> +          gimple_call_fn (stmt), direct_call->decl, stmt);
>>> +      gimple_remove_histogram_value (cfun, stmt, histogram);
>>> +      return false;
>>>       }
>>> -      return false;
>>> -    }
>>>   -  if (!check_ic_target (stmt, direct_call))
>>> -    {
>>>         if (dump_enabled_p ())
>>> -    dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
>>> -             "Indirect call -> direct call %T => %T "
>>> -             "transformation skipped because of type mismatch: %G",
>>> -             gimple_call_fn (stmt), direct_call->decl, stmt);
>>> -      gimple_remove_histogram_value (cfun, stmt, histogram);
>>> -      return false;
>>> -    }
>>> -
>>> -  if (dump_enabled_p ())
>>> -    {
>>> -      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
>>> -               "Indirect call -> direct call "
>>> -               "%T => %T transformation on insn postponed\n",
>>> -               gimple_call_fn (stmt), direct_call->decl);
>>> -      dump_printf_loc (MSG_NOTE, stmt,
>>> -               "hist->count %" PRId64
>>> -               " hist->all %" PRId64"\n", count, all);
>>> +    {
>>> +      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
>>> +               "Indirect call -> direct call "
>>> +               "%T => %T transformation on insn postponed\n",
>>> +               gimple_call_fn (stmt), direct_call->decl);
>>> +      dump_printf_loc (MSG_NOTE, stmt,
>>> +               "hist->count %" PRId64 " hist->all %" PRId64 "\n",
>>> +               count, all);
>>> +    }
>>>       }
>>>       return true;
>>>
>>


[-- Attachment #2: better-ctor.patch --]
[-- Type: text/x-patch, Size: 734 bytes --]

diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 43187bd7a19..e38cf69716d 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -1651,16 +1651,14 @@ private:
 
 struct GTY (()) indirect_target_info
 {
+  indirect_target_info (unsigned int id, int prob):
+    common_target_id (id), common_target_probability (prob)
+  {}
+
   /* Profile_id of common target obtained from profile.  */
   unsigned int common_target_id;
   /* Probability that call will land in function with COMMON_TARGET_ID.  */
   int common_target_probability;
-
-  indirect_target_info (unsigned int id, int prob)
-  {
-    common_target_id = id;
-    common_target_probability = prob;
-  }
 };
 
 /* Structure containing additional information about an indirect call.  */

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v5] Missed function specialization + partial devirtualization
  2019-09-26  8:36             ` Martin Liška
@ 2019-09-27  7:13               ` luoxhu
  2019-10-16  3:33                 ` Ping: " luoxhu
  2019-10-22 14:24                 ` Martin Liška
  0 siblings, 2 replies; 38+ messages in thread
From: luoxhu @ 2019-09-27  7:13 UTC (permalink / raw)
  To: Martin Liška, gcc-patches; +Cc: hubicka, segher, wschmidt

[-- Attachment #1: Type: text/plain, Size: 69971 bytes --]

Hi Martin,

Thanks for your time of so many round of reviews.
It really helped me a lot.
Updated with your comments and attached for Honza's review and approve.  :)


Xiong Hu
BR

On 2019/9/26 16:36, Martin Liška wrote:
> On 9/26/19 7:23 AM, luoxhu wrote:
>> Thanks Martin,
>>
>>
>> On 2019/9/25 18:57, Martin Liška wrote:
>>> On 9/25/19 5:45 AM, luoxhu wrote:
>>>> Hi,
>>>>
>>>> Sorry for replying so late due to cauldron conference and other LTO issues
>>>> I was working on.
>>>
>>> Hello.
>>>
>>> That's fine, we still have plenty of time for patch review.
>>>
>>> Not fixed issues which I reported in v3 (and still valid in v4):
>>> - please come up with indirect_target_info::indirect_target_info and use it
>> Sorry for miss out.
> 
> Hello.
> 
> Sure, please use a contructor initialization (see my patch).
> 
>>
>>
>>> - do you need to stream out indirect_call_targets when common_target_id == 0?
>>
>> No need to stream out items with common_target_id == 0, removed the if condition in lto-cgraph.c.
> 
> Fine. Do we have a guarantee that item->common_target_id is always != 0? Please put there an assert.
> 
>>
>>>
>>> Then I'm suggesting to use vec::is_empty (please see my patch).
>> OK.  But has_multiple_indirect_call_p should return different than has_indirect_call_p as it checks more that one targets?
> 
> Sure, that was mistake in my patch from previous reply.
> 
>>
>> gcc/cgraph.c
>> /* Return true if this edge has multiple indirect call targets.  */
>>   bool
>>   cgraph_edge::has_multiple_indirect_call_p (void)
>>   {
>> -  return indirect_info && indirect_info->indirect_call_targets
>> -        && indirect_info->indirect_call_targets->length () > 1;
>> +  return (indirect_info && indirect_info->indirect_call_targets
>> +         && indirect_info->indirect_call_targets->length () > 1);
>>   }
>>
>>   /* Return true if this edge has at least one indirect call target.  */
>>   bool
>>   cgraph_edge::has_indirect_call_p (void)
>>   {
>> -  return indirect_info && indirect_info->indirect_call_targets
>> -        && indirect_info->indirect_call_targets->length ();
>> +  return (indirect_info && indirect_info->indirect_call_targets
>> +         && !indirect_info->indirect_call_targets->is_empty ());
>>   }
>>
>>>
>>> I see following failures for the tests provided:
>>> FAIL: gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c compilation,  -fprofile-generate -D_PROFILE_GENERATE
>>> FAIL: gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c compilation,  -fprofile-generate -D_PROFILE_GENERATE
>>> FAIL: gcc.dg/tree-prof/indir-call-prof-topn.c compilation,  -fprofile-generate -D_PROFILE_GENERATE
>>
>> Sorry that I forgot to remove the deprecated build option in the 3 cases
>> (also updated the scan exp check):
>> -/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate --param indir-call-topn-profile=1" } */
>> +/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate" } */
>>
>>
>> The new patch is attached.  Thanks.
> 
> Hm, looking at the gimple_ic_transform function. I think the function should always
> return false as it never does a GIMPLE transformation.
> 
> Apart from that, I'm fine with the patch. Note that I'm not the maintainer, but I bet we simplified
> the patch review to Honza significantly.
> 
> Last missing piece is probably the update ChangeLog.
> 
> Thank you for working on that,
> Martin
> 
>>
>>
>> Xiong Hu
>>
>>>
>>> Next comments follow directly in the email body:
>>>
>>>>
>>>> v4 Changes:
>>>>    1. Rebase to trunk.
>>>>    2. Remove num_of_ics and use vector's length to avoid redundancy.
>>>>    3. Update the code in ipa-profile.c to improve review feasibility.
>>>>    4. Add function has_indirect_call_p and has_multiple_indirect_call_p.
>>>>    5. For parameter control, I will leave it to next patch as it is a
>>>>       relative independent function.  Currently, maximum number of
>>>>       promotions is GCOV_TOPN_VALUES as only 4 profiling value limited
>>>>       from profile-generate, therefore minimum probability is adjusted to
>>>>       25% in value-prof.c, it was 75% also by hard code for single
>>>>       indirect target.  No control to minimal number of edge
>>>>       executions yet.  What's more, this patch is a bit large now.
>>>>
>>>> This patch aims to fix PR69678 caused by PGO indirect call profiling
>>>> performance issues.
>>>> The bug that profiling data is never working was fixed by Martin's pull
>>>> back of topN patches, performance got GEOMEAN ~1% improvement(+24% for
>>>> 511.povray_r specifically).
>>>> Still, currently the default profile only generates SINGLE indirect target
>>>> that called more than 75%.  This patch leverages MULTIPLE indirect
>>>> targets use in LTO-WPA and LTO-LTRANS stage, as a result, function
>>>> specialization, profiling, partial devirtualization, inlining and
>>>> cloning could be done successfully based on it.
>>>> Performance can get improved from 0.70 sec to 0.38 sec on simple tests.
>>>> Details are:
>>>>     1.  PGO with topn is enabled by default now, but only one indirect
>>>>     target edge will be generated in ipa-profile pass, so add variables to enable
>>>>     multiple speculative edges through passes, speculative_id will record the
>>>>     direct edge index bind to the indirect edge, indirect_call_targets length
>>>>     records how many direct edges owned by the indirect edge, postpone gimple_ic
>>>>     to ipa-profile like default as inline pass will decide whether it is benefit
>>>>     to transform indirect call.
>>>>     2.  Use speculative_id to track and search the reference node matched
>>>>     with the direct edge's callee for multiple targets.  Actually, it is the
>>>>     caller's responsibility to handle the direct edges mapped to same indirect
>>>>     edge.  speculative_call_info will return one of the direct edge specified,
>>>>     this will leverage current IPA edge process framework mostly.
>>>>     3.  Enable LTO WPA/LTRANS stage multiple indirect call targets analysis for
>>>>     profile full support in ipa passes and cgraph_edge functions.  speculative_id
>>>>     can be set by make_speculative id when multiple targets are binded to
>>>>     one indirect edge, and cloned if new edge is cloned.  speculative_id
>>>>     is streamed out and stream int by lto like lto_stmt_uid.
>>>>     4.  Add 1 in module testcase and 2 cross module testcases.
>>>>     5.  Bootstrap and regression test passed on Power8-LE.  No function
>>>>     and performance regression for SPEC2017.
>>>>
>>>> gcc/ChangeLog
>>>>
>>>>      2019-09-25  Xiong Hu Luo  <luoxhu@linux.ibm.com>
>>>>
>>>>      PR ipa/69678
>>>>      * cgraph.c (symbol_table::create_edge): Init speculative_id.
>>>>      (cgraph_edge::make_speculative): Add param for setting speculative_id.
>>>>      (cgraph_edge::speculative_call_info): Find reference by
>>>>      speculative_id for multiple indirect targets.
>>>>      (cgraph_edge::resolve_speculation): Decrease the speculations
>>>>      for indirect edge, drop it's speculative if not direct target
>>>>      left.
>>>>      (cgraph_edge::redirect_call_stmt_to_callee): Likewise.
>>>>      (cgraph_node::verify_node): Don't report error if speculative
>>>>      edge not include statement.
>>>>      (cgraph_edge::has_multiple_indirect_call_p): New function.
>>>>      (cgraph_edge::has_indirect_call_p): New function.
>>>>      * cgraph.h (struct indirect_target_info): New struct.
>>>>      (indirect_call_targets): New vector variable.
>>>>      (make_speculative): Add param for setting speculative_id.
>>>>      (cgraph_edge::has_multiple_indirect_call_p): New declare.
>>>>      (cgraph_edge::has_indirect_call_p): New declare.
>>>>      (speculative_id): New variable.
>>>>      * cgraphclones.c (cgraph_node::create_clone): Clone speculative_id.
>>>>      * ipa-inline.c (inline_small_functions): Fix iterator update.
>>>>      * ipa-profile.c (ipa_profile_generate_summary): Add indirect
>>>>      multiple targets logic.
>>>>      (ipa_profile): Likewise.
>>>>      * ipa-ref.h (speculative_id): New variable.
>>>>      * ipa.c (process_references): Fix typo.
>>>>      * lto-cgraph.c (lto_output_edge): Add indirect multiple targets
>>>>      logic.  Stream out speculative_id.
>>>>      (input_edge): Likewise.
>>>>      * predict.c (dump_prediction): Remove edges count assert to be
>>>>      precise.
>>>>      * symtab.c (symtab_node::create_reference): Init speculative_id.
>>>>      (symtab_node::clone_references): Clone speculative_id.
>>>>      (symtab_node::clone_referring): Clone speculative_id.
>>>>      (symtab_node::clone_reference): Clone speculative_id.
>>>>      (symtab_node::clear_stmts_in_references): Clear speculative_id.
>>>>      * tree-inline.c (copy_bb): Duplicate all the speculative edges
>>>>      if indirect call contains multiple speculative targets.
>>>>      * tree-profile.c (gimple_gen_ic_profiler): Use the new variable
>>>>      __gcov_indirect_call.counters and __gcov_indirect_call.callee.
>>>>      (gimple_gen_ic_func_profiler): Likewise.
>>>>      (pass_ipa_tree_profile::gate): Fix comment typos.
>>>>      * value-prof.c  (gimple_ic_transform): Handle topn case.
>>>>      Fix comment typos.
>>>>
>>>> gcc/testsuite/ChangeLog
>>>>
>>>>      2019-09-25  Xiong Hu Luo  <luoxhu@linux.ibm.com>
>>>>
>>>>      PR ipa/69678
>>>>      * gcc.dg/tree-prof/indir-call-prof-topn.c: New testcase.
>>>>      * gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c: New testcase.
>>>>      * gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c: New testcase.
>>>>      * gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c: New testcase.
>>>> ---
>>>>    gcc/cgraph.c                                  | 90 ++++++++++++++++-
>>>>    gcc/cgraph.h                                  | 29 +++++-
>>>>    gcc/cgraphclones.c                            |  1 +
>>>>    gcc/ipa-inline.c                              | 15 +--
>>>>    gcc/ipa-profile.c                             | 96 ++++++++++++++-----
>>>>    gcc/ipa-ref.h                                 |  1 +
>>>>    gcc/ipa.c                                     |  2 +-
>>>>    gcc/lto-cgraph.c                              | 57 +++++++++--
>>>>    gcc/predict.c                                 |  1 -
>>>>    gcc/symtab.c                                  |  5 +
>>>>    .../tree-prof/crossmodule-indir-call-topn-1.c | 35 +++++++
>>>>    .../crossmodule-indir-call-topn-1a.c          | 22 +++++
>>>>    .../tree-prof/crossmodule-indir-call-topn-2.c | 42 ++++++++
>>>>    .../gcc.dg/tree-prof/indir-call-prof-topn.c   | 38 ++++++++
>>>>    gcc/tree-inline.c                             | 19 ++++
>>>>    gcc/tree-profile.c                            | 12 +--
>>>>    gcc/value-prof.c                              | 86 +++++++++--------
>>>>    17 files changed, 452 insertions(+), 99 deletions(-)
>>>>    create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
>>>>    create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
>>>>    create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
>>>>    create mode 100644 gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
>>>>
>>>> diff --git a/gcc/cgraph.c b/gcc/cgraph.c
>>>> index 843891e9e56..9a28aca435f 100644
>>>> --- a/gcc/cgraph.c
>>>> +++ b/gcc/cgraph.c
>>>> @@ -860,6 +860,7 @@ symbol_table::create_edge (cgraph_node *caller, cgraph_node *callee,
>>>>      edge->prev_callee = NULL;
>>>>      edge->next_callee = NULL;
>>>>      edge->lto_stmt_uid = 0;
>>>> +  edge->speculative_id = 0;
>>>>        edge->count = count;
>>>>    @@ -1051,7 +1052,8 @@ cgraph_edge::remove (void)
>>>>       Return direct edge created.  */
>>>>      cgraph_edge *
>>>> -cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
>>>> +cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count,
>>>> +                   unsigned int speculative_id)
>>>>    {
>>>>      cgraph_node *n = caller;
>>>>      ipa_ref *ref = NULL;
>>>> @@ -1069,11 +1071,13 @@ cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
>>>>      else
>>>>        e2->can_throw_external = can_throw_external;
>>>>      e2->lto_stmt_uid = lto_stmt_uid;
>>>> +  e2->speculative_id = speculative_id;
>>>>      e2->in_polymorphic_cdtor = in_polymorphic_cdtor;
>>>>      count -= e2->count;
>>>>      symtab->call_edge_duplication_hooks (this, e2);
>>>>      ref = n->create_reference (n2, IPA_REF_ADDR, call_stmt);
>>>>      ref->lto_stmt_uid = lto_stmt_uid;
>>>> +  ref->speculative_id = speculative_id;
>>>>      ref->speculative = speculative;
>>>>      n2->mark_address_taken ();
>>>>      return e2;
>>>> @@ -1087,6 +1091,38 @@ cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
>>>>       call) and if one of them exists, all of them must exist.
>>>>         Given speculative call edge, return all three components.
>>>> +
>>>> +   For some indirect edge, it may maps to multiple direct edges, i.e.  1:N.
>>>> +   check the speculative_id to return all the three components for specified
>>>> +   direct edge or indirect edge.
>>>> +   If input is indirect, caller of this function will get the direct edge one by
>>>> +   one, get_edge will just return one of the direct edge mapped to the indirect
>>>> +   edge, the returned direct edge will be resolved or redirected by the caller,
>>>> +   then number of indirect calls (speculations) is deceased in each access.
>>>> +   If input is direct, this function will get the indirect edge and reference
>>>> +   with matched speculative_id, the returned edge will also be resolved or
>>>> +   redirected, decrease the speculations accordingly.
>>>> +   Speculations of indirect edge will be dropped only if all direct edges
>>>> +   be handled.
>>>> +
>>>> +   e.g.  for indirect edge E statement "call call_dest":
>>>> +
>>>> +   Redirect N3 after redirected N2:
>>>> +
>>>> +   if (call_dest == N2)
>>>> +     n2 ();
>>>> +   else if (call_dest == N3)
>>>> +     n3 ();
>>>> +   else
>>>> +     call call_dest
>>>> +
>>>> +   Resolve N3 and only redirect N2:
>>>> +
>>>> +   if (call_dest == N2)
>>>> +     n2 ();
>>>> +   else
>>>> +     call call_dest
>>>> +
>>>>     */
>>>>      void
>>>> @@ -1126,7 +1162,7 @@ cgraph_edge::speculative_call_info (cgraph_edge *&direct,
>>>>        reference = NULL;
>>>>      for (i = 0; e->caller->iterate_reference (i, ref); i++)
>>>> -    if (ref->speculative
>>>> +    if (ref->speculative && ref->speculative_id == e->speculative_id
>>>>        && ((ref->stmt && ref->stmt == e->call_stmt)
>>>>            || (!ref->stmt && ref->lto_stmt_uid == e->lto_stmt_uid)))
>>>>          {
>>>> @@ -1187,7 +1223,21 @@ cgraph_edge::resolve_speculation (tree callee_decl)
>>>>             in the functions inlined through it.  */
>>>>        }
>>>>      edge->count += e2->count;
>>>> -  edge->speculative = false;
>>>> +  /* edge is indirect, e2 is direct.  If edge contains multiple speculations,
>>>> +     remove one of speculations for this indirect edge, then if edge still
>>>> +     contains direct target, keep the speculation, next direct target
>>>> +     will continue use it.  Give up speculation completely if no direct
>>>> +     target is left for this indirect edge.  */
>>>> +  if (edge->has_indirect_call_p ())
>>>> +    {
>>>> +      /* As the direct targets are sorted by decrease, delete the first target
>>>> +     when it is resolved.  */
>>>> +      edge->indirect_info->indirect_call_targets->ordered_remove (0);
>>>> +      if (!edge->indirect_info->indirect_call_targets->length ())
>>>> +    edge->speculative = false;
>>>> +    }
>>>> +  else
>>>> +    edge->speculative = false;
>>>>      e2->speculative = false;
>>>>      ref->remove_reference ();
>>>>      if (e2->indirect_unknown_callee || e2->inline_failed)
>>>> @@ -1321,7 +1371,21 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
>>>>          e->caller->set_call_stmt_including_clones (e->call_stmt, new_stmt,
>>>>                                 false);
>>>>          e->count = gimple_bb (e->call_stmt)->count;
>>>> -      e2->speculative = false;
>>>> +      /* edge is direct, e2 is indirect here.  If e2 contains multiple
>>>> +         speculations, remove one of speculations for this indirect edge,
>>>> +         then if e2 still contains direct target, keep the speculation,
>>>> +         next direct target will continue use it.  Give up speculation
>>>> +         completely if no direct target is left for this indirect e2.  */
>>>> +      if (e2->has_indirect_call_p ())
>>>> +        {
>>>> +          /* As the direct targets are sorted by decrease, delete the first
>>>> +         target when it is redirected.  */
>>>> +          e2->indirect_info->indirect_call_targets->ordered_remove (0);
>>>> +          if (!e2->indirect_info->indirect_call_targets->length ())
>>>> +        e2->speculative = false;
>>>> +        }
>>>> +      else
>>>> +        e2->speculative = false;
>>>>          e2->count = gimple_bb (e2->call_stmt)->count;
>>>>          ref->speculative = false;
>>>>          ref->stmt = NULL;
>>>> @@ -3445,7 +3509,7 @@ cgraph_node::verify_node (void)
>>>>            for (e = callees; e; e = e->next_callee)
>>>>        {
>>>> -      if (!e->aux)
>>>> +      if (!e->aux && !e->speculative)
>>>>            {
>>>>              error ("edge %s->%s has no corresponding call_stmt",
>>>>                 identifier_to_locale (e->caller->name ()),
>>>> @@ -3872,6 +3936,22 @@ cgraph_edge::possibly_call_in_translation_unit_p (void)
>>>>      return node->get_availability () >= AVAIL_AVAILABLE;
>>>>    }
>>>>    +/* Return true if this edge has multiple indirect call targets.  */
>>>> +bool
>>>> +cgraph_edge::has_multiple_indirect_call_p (void)
>>>> +{
>>>> +  return indirect_info && indirect_info->indirect_call_targets
>>>> +     && indirect_info->indirect_call_targets->length () > 1;
>>>> +}
>>>
>>> For multiline && expression, we typically wrap the whole condition
>>> in '(' and ')'.
>>>
>>>> +
>>>> +/* Return true if this edge has at least one indirect call target.  */
>>>> +bool
>>>> +cgraph_edge::has_indirect_call_p (void)
>>>> +{
>>>> +  return indirect_info && indirect_info->indirect_call_targets
>>>> +     && indirect_info->indirect_call_targets->length ();
>>>> +}
>>>
>>> Likewise here.
>>>
>>>> +
>>>>    /* A stashed copy of "symtab" for use by selftest::symbol_table_test.
>>>>       This needs to be a global so that it can be a GC root, and thus
>>>>       prevent the stashed copy from being garbage-collected if the GC runs
>>>> diff --git a/gcc/cgraph.h b/gcc/cgraph.h
>>>> index 4c54210123a..33c8454c4e0 100644
>>>> --- a/gcc/cgraph.h
>>>> +++ b/gcc/cgraph.h
>>>> @@ -1636,6 +1636,16 @@ private:
>>>>      void make_speculative (tree otr_type = NULL);
>>>>    };
>>>>    +/* Structure containing indirect target information from profile.  */
>>>> +
>>>> +struct GTY (()) indirect_target_info
>>>> +{
>>>> +  /* Profile_id of common target obtained from profile.  */
>>>> +  unsigned int common_target_id;
>>>> +  /* Probability that call will land in function with COMMON_TARGET_ID.  */
>>>> +  int common_target_probability;
>>>> +};
>>>> +
>>>>    /* Structure containing additional information about an indirect call.  */
>>>>      class GTY(()) cgraph_indirect_call_info
>>>> @@ -1654,10 +1664,9 @@ public:
>>>>      int param_index;
>>>>      /* ECF flags determined from the caller.  */
>>>>      int ecf_flags;
>>>> -  /* Profile_id of common target obtrained from profile.  */
>>>> -  int common_target_id;
>>>> -  /* Probability that call will land in function with COMMON_TARGET_ID.  */
>>>> -  int common_target_probability;
>>>> +
>>>> +  /* An indirect call may contain one or multiple call targets.  */
>>>> +  vec<indirect_target_info, va_gc> *indirect_call_targets;
>>>>        /* Set when the call is a virtual call with the parameter being the
>>>>         associated object pointer rather than a simple direct call.  */
>>>> @@ -1714,7 +1723,8 @@ public:
>>>>      /* Turn edge into speculative call calling N2. Update
>>>>         the profile so the direct call is taken COUNT times
>>>>         with FREQUENCY.  */
>>>> -  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count);
>>>> +  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count,
>>>> +                 unsigned int speculative_id = 0);
>>>>         /* Given speculative call edge, return all three components.  */
>>>>      void speculative_call_info (cgraph_edge *&direct, cgraph_edge *&indirect,
>>>> @@ -1773,6 +1783,12 @@ public:
>>>>         be internal to the current translation unit.  */
>>>>      bool possibly_call_in_translation_unit_p (void);
>>>>    +  /* Return true if this edge has multiple indirect call targets.  */
>>>> +  bool has_multiple_indirect_call_p (void);
>>>> +
>>>> +  /* Return true if this edge has at least one indirect call target.  */
>>>> +  bool has_indirect_call_p (void);
>>>> +
>>>>      /* Expected number of executions: calculated in profile.c.  */
>>>>      profile_count count;
>>>>      cgraph_node *caller;
>>>> @@ -1792,6 +1808,9 @@ public:
>>>>      /* The stmt_uid of call_stmt.  This is used by LTO to recover the call_stmt
>>>>         when the function is serialized in.  */
>>>>      unsigned int lto_stmt_uid;
>>>> +  /* speculative id is used by multiple indirect targets when the function is
>>>> +   speculated.  */
>>>> +  unsigned int speculative_id;
>>>>      /* Whether this edge was made direct by indirect inlining.  */
>>>>      unsigned int indirect_inlining_edge : 1;
>>>>      /* Whether this edge describes an indirect call with an undetermined
>>>> diff --git a/gcc/cgraphclones.c b/gcc/cgraphclones.c
>>>> index fa753697c78..5dbd8d90b77 100644
>>>> --- a/gcc/cgraphclones.c
>>>> +++ b/gcc/cgraphclones.c
>>>> @@ -128,6 +128,7 @@ cgraph_edge::clone (cgraph_node *n, gcall *call_stmt, unsigned stmt_uid,
>>>>      new_edge->inline_failed = inline_failed;
>>>>      new_edge->indirect_inlining_edge = indirect_inlining_edge;
>>>>      new_edge->lto_stmt_uid = stmt_uid;
>>>> +  new_edge->speculative_id = speculative_id;
>>>>      /* Clone flags that depend on call_stmt availability manually.  */
>>>>      new_edge->can_throw_external = can_throw_external;
>>>>      new_edge->call_stmt_cannot_inline_p = call_stmt_cannot_inline_p;
>>>> diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
>>>> index b62d280eb25..6136214f9ac 100644
>>>> --- a/gcc/ipa-inline.c
>>>> +++ b/gcc/ipa-inline.c
>>>> @@ -1881,12 +1881,15 @@ inline_small_functions (void)
>>>>        }
>>>>          if (has_speculative)
>>>>        for (edge = node->callees; edge; edge = next)
>>>> -      if (edge->speculative && !speculation_useful_p (edge,
>>>> -                              edge->aux != NULL))
>>>> -        {
>>>> -          edge->resolve_speculation ();
>>>> -          update = true;
>>>> -        }
>>>> +      {
>>>> +        next = edge->next_callee;
>>>> +        if (edge->speculative
>>>> +        && !speculation_useful_p (edge, edge->aux != NULL))
>>>> +          {
>>>> +        edge->resolve_speculation ();
>>>> +        update = true;
>>>> +          }
>>>> +      }
>>>>          if (update)
>>>>        {
>>>>          struct cgraph_node *where = node->global.inlined_to
>>>> diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
>>>> index 970dba39c80..342e8ea05d1 100644
>>>> --- a/gcc/ipa-profile.c
>>>> +++ b/gcc/ipa-profile.c
>>>> @@ -192,23 +192,35 @@ ipa_profile_generate_summary (void)
>>>>              if (h)
>>>>                {
>>>>                  gcov_type val, count, all;
>>>> -              if (get_nth_most_common_value (NULL, "indirect call", h,
>>>> -                             &val, &count, &all))
>>>> +              struct cgraph_edge *e = node->get_edge (stmt);
>>>> +              if (e && !e->indirect_unknown_callee)
>>>> +            continue;
>>>> +
>>>> +              struct indirect_target_info item;
>>>> +              for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
>>>>                {
>>>> -              struct cgraph_edge * e = node->get_edge (stmt);
>>>> -              if (e && !e->indirect_unknown_callee)
>>>> +              if (!get_nth_most_common_value (NULL, "indirect call",
>>>> +                              h, &val, &count, &all,
>>>> +                              j))
>>>> +                continue;
>>>> +
>>>> +              if (val == 0)
>>>>                    continue;
>>>>    -              e->indirect_info->common_target_id = val;
>>>> -              e->indirect_info->common_target_probability
>>>> +              item.common_target_id = val;
>>>> +              item.common_target_probability
>>>>                    = GCOV_COMPUTE_SCALE (count, all);
>>>
>>> There's one of the places where you can use the constructor.
>>>
>>>> -              if (e->indirect_info->common_target_probability > REG_BR_PROB_BASE)
>>>> +              if (item.common_target_probability > REG_BR_PROB_BASE)
>>>>                    {
>>>>                      if (dump_file)
>>>> -                fprintf (dump_file, "Probability capped to 1\n");
>>>> -                  e->indirect_info->common_target_probability = REG_BR_PROB_BASE;
>>>> +                fprintf (dump_file,
>>>> +                     "Probability capped to 1\n");
>>>> +                  item.common_target_probability = REG_BR_PROB_BASE;
>>>>                    }
>>>> +              vec_safe_push (
>>>> +                e->indirect_info->indirect_call_targets, item);
>>>>                }
>>>> +
>>>>                  gimple_remove_histogram_value (DECL_STRUCT_FUNCTION (node->decl),
>>>>                                  stmt, h);
>>>>                }
>>>> @@ -492,6 +504,7 @@ ipa_profile (void)
>>>>      int nindirect = 0, ncommon = 0, nunknown = 0, nuseless = 0, nconverted = 0;
>>>>      int nmismatch = 0, nimpossible = 0;
>>>>      bool node_map_initialized = false;
>>>> +  gcov_type threshold;
>>>>        if (dump_file)
>>>>        dump_histogram (dump_file, histogram);
>>>> @@ -500,14 +513,12 @@ ipa_profile (void)
>>>>          overall_time += histogram[i]->count * histogram[i]->time;
>>>>          overall_size += histogram[i]->size;
>>>>        }
>>>> +  threshold = 0;
>>>>      if (overall_time)
>>>>        {
>>>> -      gcov_type threshold;
>>>> -
>>>>          gcc_assert (overall_size);
>>>>            cutoff = (overall_time * PARAM_VALUE (HOT_BB_COUNT_WS_PERMILLE) + 500) / 1000;
>>>> -      threshold = 0;
>>>>          for (i = 0; cumulated < cutoff; i++)
>>>>        {
>>>>          cumulated += histogram[i]->count * histogram[i]->time;
>>>> @@ -543,7 +554,7 @@ ipa_profile (void)
>>>>      histogram.release ();
>>>>      histogram_pool.release ();
>>>>    -  /* Produce speculative calls: we saved common traget from porfiling into
>>>> +  /* Produce speculative calls: we saved common target from profiling into
>>>>         e->common_target_id.  Now, at link time, we can look up corresponding
>>>>         function node and produce speculative call.  */
>>>>    @@ -558,13 +569,37 @@ ipa_profile (void)
>>>>        {
>>>>          if (n->count.initialized_p ())
>>>>            nindirect++;
>>>> -      if (e->indirect_info->common_target_id)
>>>> +      if (e->has_indirect_call_p ())
>>>>            {
>>>>              if (!node_map_initialized)
>>>> -            init_node_map (false);
>>>> +        init_node_map (false);
>>>>              node_map_initialized = true;
>>>>              ncommon++;
>>>> -          n2 = find_func_by_profile_id (e->indirect_info->common_target_id);
>>>> +
>>>> +          if (in_lto_p)
>>>> +        {
>>>> +          if (dump_file)
>>>> +            {
>>>> +              fprintf (dump_file,
>>>> +                   "Updating hotness threshold in LTO mode.\n");
>>>> +              fprintf (dump_file, "Updated min count: %" PRId64 "\n",
>>>> +                   (int64_t) threshold);
>>>> +            }
>>>> +          set_hot_bb_threshold (threshold
>>>> +            / e->indirect_info->indirect_call_targets->length ());
>>>> +        }
>>>> +
>>>> +          unsigned speculative_id = 0;
>>>> +          struct indirect_target_info *item;
>>>> +          /* The code below is not formatted yet for review convenience.
>>>> +         Move to a seprate small function is not easy as too many local
>>>> +         variables used in it.  Need format and remove this comments
>>>> +         once got approved.  */
>>>> +          FOR_EACH_VEC_SAFE_ELT (e->indirect_info->indirect_call_targets, i,
>>>> +                     item)
>>>> +           {
>>>> +          bool speculative_found = false;
>>>> +          n2 = find_func_by_profile_id (item->common_target_id);
>>>>              if (n2)
>>>>            {
>>>>              if (dump_file)
>>>> @@ -573,11 +608,10 @@ ipa_profile (void)
>>>>                       " other module %s => %s, prob %3.2f\n",
>>>>                       n->dump_name (),
>>>>                       n2->dump_name (),
>>>> -                   e->indirect_info->common_target_probability
>>>> -                   / (float)REG_BR_PROB_BASE);
>>>> +                   item->common_target_probability
>>>> +                 / (float) REG_BR_PROB_BASE);
>>>>                }
>>>> -          if (e->indirect_info->common_target_probability
>>>> -              < REG_BR_PROB_BASE / 2)
>>>> +          if (item->common_target_probability < REG_BR_PROB_BASE / 2)
>>>>                {
>>>>                  nuseless++;
>>>>                  if (dump_file)
>>>> @@ -613,7 +647,7 @@ ipa_profile (void)
>>>>                  if (dump_file)
>>>>                fprintf (dump_file,
>>>>                     "Not speculating: "
>>>> -                 "parameter count mistmatch\n");
>>>> +                 "parameter count mismatch\n");
>>>>                }
>>>>              else if (e->indirect_info->polymorphic
>>>>                   && !opt_for_fn (n->decl, flag_devirtualize)
>>>> @@ -640,20 +674,30 @@ ipa_profile (void)
>>>>                    n2 = alias;
>>>>                }
>>>>                  nconverted++;
>>>> -              e->make_speculative
>>>> -            (n2,
>>>> -             e->count.apply_probability
>>>> -                     (e->indirect_info->common_target_probability));
>>>> +              e->make_speculative (n2,
>>>> +                       e->count.apply_probability (
>>>> +                         item->common_target_probability),
>>>> +                       speculative_id);
>>>>                  update = true;
>>>> +              speculative_id++;
>>>> +              speculative_found = true;
>>>>                }
>>>>            }
>>>>              else
>>>>            {
>>>>              if (dump_file)
>>>>                fprintf (dump_file, "Function with profile-id %i not found.\n",
>>>> -                 e->indirect_info->common_target_id);
>>>> +                 item->common_target_id);
>>>>              nunknown++;
>>>>            }
>>>> +          if (!speculative_found)
>>>> +        {
>>>> +          /* Remove item from indirect_call_targets if no
>>>> +             speculative edge generated, rollback the iteration.  */
>>>> +          e->indirect_info->indirect_call_targets->ordered_remove (i);
>>>> +          i--;
>>>> +        }
>>>> +           }
>>>>            }
>>>>         }
>>>>           if (update)
>>>> diff --git a/gcc/ipa-ref.h b/gcc/ipa-ref.h
>>>> index 0d8e509c932..3e6562ec9d1 100644
>>>> --- a/gcc/ipa-ref.h
>>>> +++ b/gcc/ipa-ref.h
>>>> @@ -59,6 +59,7 @@ public:
>>>>      symtab_node *referred;
>>>>      gimple *stmt;
>>>>      unsigned int lto_stmt_uid;
>>>> +  unsigned int speculative_id;
>>>>      unsigned int referred_index;
>>>>      ENUM_BITFIELD (ipa_ref_use) use:3;
>>>>      unsigned int speculative:1;
>>>> diff --git a/gcc/ipa.c b/gcc/ipa.c
>>>> index 6b84e1f9bda..a10b0603f14 100644
>>>> --- a/gcc/ipa.c
>>>> +++ b/gcc/ipa.c
>>>> @@ -166,7 +166,7 @@ process_references (symtab_node *snode,
>>>>       devirtualization happens.  After inlining still keep their declarations
>>>>       around, so we can devirtualize to a direct call.
>>>>    -   Also try to make trivial devirutalization when no or only one target is
>>>> +   Also try to make trivial devirtualization when no or only one target is
>>>>       possible.  */
>>>>      static void
>>>> diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
>>>> index bc0f0107333..61380dcc7b8 100644
>>>> --- a/gcc/lto-cgraph.c
>>>> +++ b/gcc/lto-cgraph.c
>>>> @@ -238,6 +238,7 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
>>>>      unsigned int uid;
>>>>      intptr_t ref;
>>>>      struct bitpack_d bp;
>>>> +  unsigned len;
>>>>        if (edge->indirect_unknown_callee)
>>>>        streamer_write_enum (ob->main_stream, LTO_symtab_tags, LTO_symtab_last_tag,
>>>> @@ -265,6 +266,7 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
>>>>      bp_pack_enum (&bp, cgraph_inline_failed_t,
>>>>                CIF_N_REASONS, edge->inline_failed);
>>>>      bp_pack_var_len_unsigned (&bp, uid);
>>>> +  bp_pack_var_len_unsigned (&bp, edge->speculative_id);
>>>>      bp_pack_value (&bp, edge->indirect_inlining_edge, 1);
>>>>      bp_pack_value (&bp, edge->speculative, 1);
>>>>      bp_pack_value (&bp, edge->call_stmt_cannot_inline_p, 1);
>>>> @@ -291,11 +293,27 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
>>>>      streamer_write_bitpack (&bp);
>>>>      if (edge->indirect_unknown_callee)
>>>>        {
>>>> -      streamer_write_hwi_stream (ob->main_stream,
>>>> -                     edge->indirect_info->common_target_id);
>>>> -      if (edge->indirect_info->common_target_id)
>>>> -    streamer_write_hwi_stream
>>>> -       (ob->main_stream, edge->indirect_info->common_target_probability);
>>>> +      struct indirect_target_info *item;
>>>> +      unsigned int i;
>>>> +      len = edge->has_indirect_call_p ()
>>>> +          ? edge->indirect_info->indirect_call_targets->length ()
>>>> +          : 0;
>>>> +      gcc_assert (len <= GCOV_TOPN_VALUES);
>>>> +
>>>> +      streamer_write_hwi_stream (ob->main_stream, len);
>>>> +
>>>> +      if (len)
>>>> +    {
>>>> +      FOR_EACH_VEC_SAFE_ELT (edge->indirect_info->indirect_call_targets, i,
>>>> +                 item)
>>>> +        {
>>>> +          streamer_write_hwi_stream (ob->main_stream,
>>>> +                     item->common_target_id);
>>>> +          if (item->common_target_id)
>>>> +        streamer_write_hwi_stream (ob->main_stream,
>>>> +                       item->common_target_probability);
>>>> +        }
>>>> +    }
>>>>        }
>>>>    }
>>>>    @@ -688,6 +706,7 @@ lto_output_ref (struct lto_simple_output_block *ob, struct ipa_ref *ref,
>>>>          if (ref->stmt)
>>>>        uid = gimple_uid (ref->stmt) + 1;
>>>>          streamer_write_hwi_stream (ob->main_stream, uid);
>>>> +      streamer_write_hwi_stream (ob->main_stream, ref->speculative_id);
>>>>        }
>>>>    }
>>>>    @@ -1419,7 +1438,10 @@ input_ref (class lto_input_block *ib,
>>>>      ref = referring_node->create_reference (node, use);
>>>>      ref->speculative = speculative;
>>>>      if (is_a <cgraph_node *> (referring_node))
>>>> -    ref->lto_stmt_uid = streamer_read_hwi (ib);
>>>> +    {
>>>> +      ref->lto_stmt_uid = streamer_read_hwi (ib);
>>>> +      ref->speculative_id = streamer_read_hwi (ib);
>>>> +    }
>>>>    }
>>>>      /* Read an edge from IB.  NODES points to a vector of previously read nodes for
>>>> @@ -1433,11 +1455,12 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
>>>>    {
>>>>      struct cgraph_node *caller, *callee;
>>>>      struct cgraph_edge *edge;
>>>> -  unsigned int stmt_id;
>>>> +  unsigned int stmt_id, speculative_id;
>>>>      profile_count count;
>>>>      cgraph_inline_failed_t inline_failed;
>>>>      struct bitpack_d bp;
>>>>      int ecf_flags = 0;
>>>> +  unsigned i, len;
>>>>        caller = dyn_cast<cgraph_node *> (nodes[streamer_read_hwi (ib)]);
>>>>      if (caller == NULL || caller->decl == NULL_TREE)
>>>> @@ -1457,6 +1480,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
>>>>      bp = streamer_read_bitpack (ib);
>>>>      inline_failed = bp_unpack_enum (&bp, cgraph_inline_failed_t, CIF_N_REASONS);
>>>>      stmt_id = bp_unpack_var_len_unsigned (&bp);
>>>> +  speculative_id = bp_unpack_var_len_unsigned (&bp);
>>>>        if (indirect)
>>>>        edge = caller->create_indirect_edge (NULL, 0, count);
>>>> @@ -1466,6 +1490,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
>>>>      edge->indirect_inlining_edge = bp_unpack_value (&bp, 1);
>>>>      edge->speculative = bp_unpack_value (&bp, 1);
>>>>      edge->lto_stmt_uid = stmt_id;
>>>> +  edge->speculative_id = speculative_id;
>>>>      edge->inline_failed = inline_failed;
>>>>      edge->call_stmt_cannot_inline_p = bp_unpack_value (&bp, 1);
>>>>      edge->can_throw_external = bp_unpack_value (&bp, 1);
>>>> @@ -1485,9 +1510,21 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
>>>>          if (bp_unpack_value (&bp, 1))
>>>>        ecf_flags |= ECF_RETURNS_TWICE;
>>>>          edge->indirect_info->ecf_flags = ecf_flags;
>>>> -      edge->indirect_info->common_target_id = streamer_read_hwi (ib);
>>>> -      if (edge->indirect_info->common_target_id)
>>>> -        edge->indirect_info->common_target_probability = streamer_read_hwi (ib);
>>>> +
>>>> +      len = streamer_read_hwi (ib);
>>>> +
>>>> +      gcc_assert (len <= GCOV_TOPN_VALUES);
>>>> +
>>>> +      if (len)
>>>> +    {
>>>> +      indirect_target_info item;
>>>> +      for (i = 0; i < len; i++)
>>>> +        {
>>>> +          item.common_target_id = streamer_read_hwi (ib);
>>>> +          item.common_target_probability = streamer_read_hwi (ib);
>>>> +          vec_safe_push (edge->indirect_info->indirect_call_targets, item);
>>>> +        }
>>>> +    }
>>>>        }
>>>>    }
>>>>    diff --git a/gcc/predict.c b/gcc/predict.c
>>>> index 915f0806b11..3f56fa3a74a 100644
>>>> --- a/gcc/predict.c
>>>> +++ b/gcc/predict.c
>>>> @@ -762,7 +762,6 @@ dump_prediction (FILE *file, enum br_predictor predictor, int probability,
>>>>          && bb->count.precise_p ()
>>>>          && reason == REASON_NONE)
>>>>        {
>>>> -      gcc_assert (e->count ().precise_p ());
>>>>          fprintf (file, ";;heuristics;%s;%" PRId64 ";%" PRId64 ";%.1f;\n",
>>>>               predictor_info[predictor].name,
>>>>               bb->count.to_gcov_type (), e->count ().to_gcov_type (),
>>>> diff --git a/gcc/symtab.c b/gcc/symtab.c
>>>> index ee9723c3453..d4c36fd3e5a 100644
>>>> --- a/gcc/symtab.c
>>>> +++ b/gcc/symtab.c
>>>> @@ -603,6 +603,7 @@ symtab_node::create_reference (symtab_node *referred_node,
>>>>      ref->referred = referred_node;
>>>>      ref->stmt = stmt;
>>>>      ref->lto_stmt_uid = 0;
>>>> +  ref->speculative_id = 0;
>>>>      ref->use = use_type;
>>>>      ref->speculative = 0;
>>>>    @@ -660,6 +661,7 @@ symtab_node::clone_references (symtab_node *node)
>>>>          ref2 = create_reference (ref->referred, ref->use, ref->stmt);
>>>>          ref2->speculative = speculative;
>>>>          ref2->lto_stmt_uid = stmt_uid;
>>>> +      ref2->speculative_id = ref->speculative_id;
>>>>        }
>>>>    }
>>>>    @@ -678,6 +680,7 @@ symtab_node::clone_referring (symtab_node *node)
>>>>          ref2 = ref->referring->create_reference (this, ref->use, ref->stmt);
>>>>          ref2->speculative = speculative;
>>>>          ref2->lto_stmt_uid = stmt_uid;
>>>> +      ref2->speculative_id = ref->speculative_id;
>>>>        }
>>>>    }
>>>>    @@ -693,6 +696,7 @@ symtab_node::clone_reference (ipa_ref *ref, gimple *stmt)
>>>>      ref2 = create_reference (ref->referred, ref->use, stmt);
>>>>      ref2->speculative = speculative;
>>>>      ref2->lto_stmt_uid = stmt_uid;
>>>> +  ref2->speculative_id = ref->speculative_id;
>>>>      return ref2;
>>>>    }
>>>>    @@ -747,6 +751,7 @@ symtab_node::clear_stmts_in_references (void)
>>>>          {
>>>>        r->stmt = NULL;
>>>>        r->lto_stmt_uid = 0;
>>>> +    r->speculative_id = 0;
>>>>          }
>>>>    }
>>>>    diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
>>>> new file mode 100644
>>>> index 00000000000..e0a83c2e067
>>>> --- /dev/null
>>>> +++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
>>>> @@ -0,0 +1,35 @@
>>>> +/* { dg-require-effective-target lto } */
>>>> +/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
>>>> +/* { dg-require-profiling "-fprofile-generate" } */
>>>> +/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate --param indir-call-topn-profile=1" } */
>>>> +
>>>> +#include <stdio.h>
>>>> +
>>>> +typedef int (*fptr) (int);
>>>> +int
>>>> +one (int a);
>>>> +
>>>> +int
>>>> +two (int a);
>>>> +
>>>> +fptr table[] = {&one, &two};
>>>> +
>>>> +int
>>>> +main()
>>>> +{
>>>> +  int i, x;
>>>> +  fptr p = &one;
>>>> +
>>>> +  x = one (3);
>>>> +
>>>> +  for (i = 0; i < 350000000; i++)
>>>> +    {
>>>> +      x = (*p) (3);
>>>> +      p = table[x];
>>>> +    }
>>>> +  printf ("done:%d\n", x);
>>>> +}
>>>> +
>>>> +/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* one transformation on insn" "profile_estimate" } } */
>>>> +/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* two transformation on insn" "profile_estimate" } } */
>>>> +
>>>> diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
>>>> new file mode 100644
>>>> index 00000000000..a8c6e365fb9
>>>> --- /dev/null
>>>> +++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
>>>> @@ -0,0 +1,22 @@
>>>> +/* It seems there is no way to avoid the other source of mulitple
>>>> +   source testcase from being compiled independently.  Just avoid
>>>> +   error.  */
>>>> +#ifdef DOJOB
>>>> +int
>>>> +one (int a)
>>>> +{
>>>> +  return 1;
>>>> +}
>>>> +
>>>> +int
>>>> +two (int a)
>>>> +{
>>>> +  return 0;
>>>> +}
>>>> +#else
>>>> +int
>>>> +main()
>>>> +{
>>>> +  return 0;
>>>> +}
>>>> +#endif
>>>> diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
>>>> new file mode 100644
>>>> index 00000000000..aa3887fde83
>>>> --- /dev/null
>>>> +++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
>>>> @@ -0,0 +1,42 @@
>>>> +/* { dg-require-effective-target lto } */
>>>> +/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
>>>> +/* { dg-require-profiling "-fprofile-generate" } */
>>>> +/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate --param indir-call-topn-profile=1" } */
>>>> +
>>>> +#include <stdio.h>
>>>> +
>>>> +typedef int (*fptr) (int);
>>>> +int
>>>> +one (int a);
>>>> +
>>>> +int
>>>> +two (int a);
>>>> +
>>>> +fptr table[] = {&one, &two};
>>>> +
>>>> +int foo ()
>>>> +{
>>>> +  int i, x;
>>>> +  fptr p = &one;
>>>> +
>>>> +  x = one (3);
>>>> +
>>>> +  for (i = 0; i < 350000000; i++)
>>>> +    {
>>>> +      x = (*p) (3);
>>>> +      p = table[x];
>>>> +    }
>>>> +  return x;
>>>> +}
>>>> +
>>>> +int
>>>> +main()
>>>> +{
>>>> +  int x = foo ();
>>>> +  printf ("done:%d\n", x);
>>>> +}
>>>> +
>>>> +/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* one transformation on insn" "profile_estimate" } } */
>>>> +/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> direct call.* two transformation on insn" "profile_estimate" } } */
>>>> +
>>>> +
>>>> diff --git a/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
>>>> new file mode 100644
>>>> index 00000000000..951bc7ddd19
>>>> --- /dev/null
>>>> +++ b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
>>>> @@ -0,0 +1,38 @@
>>>> +/* { dg-require-profiling "-fprofile-generate" } */
>>>> +/* { dg-options "-O2 -fdump-ipa-profile --param indir-call-topn-profile=1" } */
>>>> +
>>>> +#include <stdio.h>
>>>> +
>>>> +typedef int (*fptr) (int);
>>>> +int
>>>> +one (int a)
>>>> +{
>>>> +  return 1;
>>>> +}
>>>> +
>>>> +int
>>>> +two (int a)
>>>> +{
>>>> +  return 0;
>>>> +}
>>>> +
>>>> +fptr table[] = {&one, &two};
>>>> +
>>>> +int
>>>> +main()
>>>> +{
>>>> +  int i, x;
>>>> +  fptr p = &one;
>>>> +
>>>> +  one (3);
>>>> +
>>>> +  for (i = 0; i < 350000000; i++)
>>>> +    {
>>>> +      x = (*p) (3);
>>>> +      p = table[x];
>>>> +    }
>>>> +  printf ("done:%d\n", x);
>>>> +}
>>>> +
>>>> +/* { dg-final-use-not-autofdo { scan-ipa-dump "Indirect call -> direct call.* one transformation on insn" "profile" } } */
>>>> +/* { dg-final-use-not-autofdo { scan-ipa-dump "Indirect call -> direct call.* two transformation on insn" "profile" } } */
>>>> diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
>>>> index b9c1a3b1456..fe3e172fbd1 100644
>>>> --- a/gcc/tree-inline.c
>>>> +++ b/gcc/tree-inline.c
>>>> @@ -2167,6 +2167,25 @@ copy_bb (copy_body_data *id, basic_block bb,
>>>>                    gcc_assert (!edge->indirect_unknown_callee);
>>>>                  old_edge->speculative_call_info (direct, indirect, ref);
>>>> +              while (old_edge->next_callee
>>>> +                 && old_edge->next_callee->speculative
>>>> +                 && indirect->has_multiple_indirect_call_p ())
>>>> +                {
>>>> +                  /* Some speculative calls may contain more than
>>>> +                 one direct target, loop iterate it to clone all
>>>> +                 related direct edges before cloning the related
>>>> +                 indirect edge.  */
>>>> +                  id->dst_node->clone_reference (ref, stmt);
>>>> +
>>>> +                  edge = old_edge->next_callee;
>>>> +                  edge = edge->clone (id->dst_node, call_stmt,
>>>> +                          gimple_uid (stmt), num, den,
>>>> +                          true);
>>>> +                  old_edge = old_edge->next_callee;
>>>> +                  gcc_assert (!edge->indirect_unknown_callee);
>>>> +                  old_edge->speculative_call_info (direct, indirect,
>>>> +                                   ref);
>>>> +                }
>>>>                    profile_count indir_cnt = indirect->count;
>>>>                  indirect = indirect->clone (id->dst_node, call_stmt,
>>>> diff --git a/gcc/tree-profile.c b/gcc/tree-profile.c
>>>> index 4c1ead5781f..ef7748668f8 100644
>>>> --- a/gcc/tree-profile.c
>>>> +++ b/gcc/tree-profile.c
>>>> @@ -74,8 +74,8 @@ static GTY(()) tree ic_tuple_callee_field;
>>>>    /* Do initialization work for the edge profiler.  */
>>>>      /* Add code:
>>>> -   __thread gcov*    __gcov_indirect_call_counters; // pointer to actual counter
>>>> -   __thread void*    __gcov_indirect_call_callee; // actual callee address
>>>> +   __thread gcov*    __gcov_indirect_call.counters; // pointer to actual counter
>>>> +   __thread void*    __gcov_indirect_call.callee; // actual callee address
>>>>       __thread int __gcov_function_counter; // time profiler function counter
>>>>    */
>>>>    static void
>>>> @@ -382,7 +382,7 @@ gimple_gen_ic_profiler (histogram_value value, unsigned tag)
>>>>          f_1 = foo;
>>>>          __gcov_indirect_call.counters = &__gcov4.main[0];
>>>>          PROF_9 = f_1;
>>>> -      __gcov_indirect_call_callee = PROF_9;
>>>> +      __gcov_indirect_call.callee = PROF_9;
>>>>          _4 = f_1 ();
>>>>       */
>>>>    @@ -445,11 +445,11 @@ gimple_gen_ic_func_profiler (void)
>>>>        /* Insert code:
>>>>    -     if (__gcov_indirect_call_callee != NULL)
>>>> +     if (__gcov_indirect_call.callee != NULL)
>>>>           __gcov_indirect_call_profiler_v3 (profile_id, &current_function_decl);
>>>>           The function __gcov_indirect_call_profiler_v3 is responsible for
>>>> -     resetting __gcov_indirect_call_callee to NULL.  */
>>>> +     resetting __gcov_indirect_call.callee to NULL.  */
>>>>        gimple_stmt_iterator gsi = gsi_start_bb (cond_bb);
>>>>      void0 = build_int_cst (ptr_type_node, 0);
>>>> @@ -891,7 +891,7 @@ pass_ipa_tree_profile::gate (function *)
>>>>    {
>>>>      /* When profile instrumentation, use or test coverage shall be performed.
>>>>         But for AutoFDO, this there is no instrumentation, thus this pass is
>>>> -     diabled.  */
>>>> +     disabled.  */
>>>>      return (!in_lto_p && !flag_auto_profile
>>>>          && (flag_branch_probabilities || flag_test_coverage
>>>>              || profile_arc_flag));
>>>> diff --git a/gcc/value-prof.c b/gcc/value-prof.c
>>>> index 55ea0973a03..0588df0fce9 100644
>>>> --- a/gcc/value-prof.c
>>>> +++ b/gcc/value-prof.c
>>>> @@ -1406,11 +1406,10 @@ gimple_ic (gcall *icall_stmt, struct cgraph_node *direct_call,
>>>>      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:
>>>> - */
>>>> +/* There maybe multiple indirect targets in histogram.  Check every
>>>> +   indirect/virtual call if callee function exists, if not exist, leave it to
>>>> +   LTO stage for later process.  Modify code of this indirect call to an if-else
>>>> +   structure in ipa-profile finally.  */
>>>>      static bool
>>>>    gimple_ic_transform (gimple_stmt_iterator *gsi)
>>>> @@ -1434,48 +1433,57 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
>>>>      if (!histogram)
>>>>        return false;
>>>
>>> The function is not correct, note that the function can now return true
>>> when this transformation happens:
>>>            "Indirect call -> direct call from other "
>>>            "module %T=> %i (will resolve only with LTO)\n",
>>>
>>> Current trunk returns false in that case.
>>>
>>>>    -  if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
>>>> -                  &count, &all))
>>>> -    return false;
>>>> +  count = 0;
>>>> +  all = histogram->hvalue.counters[0];
>>>>    -  if (4 * count <= 3 * all)
>>>> -    return false;
>>>> +  for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
>>>> +    {
>>>> +      if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
>>>> +                      &count, &all, j))
>>>> +    continue;
>>>
>>> You should break here as get_nth_most_common_value (..., j + 1) will also return
>>> false.
>>>
>>>>    -  direct_call = find_func_by_profile_id ((int)val);
>>>> +      /* Minimum probability.  should be higher than 25%.  */
>>>> +      if (4 * count <= all)
>>>> +    continue;
>>>
>>> You can break here as well.
>>>
>>> Thank you,
>>> Martin
>>>
>>>>    -  if (direct_call == NULL)
>>>> -    {
>>>> -      if (val)
>>>> +      direct_call = find_func_by_profile_id ((int) val);
>>>> +
>>>> +      if (direct_call == NULL)
>>>> +    {
>>>> +      if (val)
>>>> +        {
>>>> +          if (dump_enabled_p ())
>>>> +        dump_printf_loc (
>>>> +          MSG_MISSED_OPTIMIZATION, stmt,
>>>> +          "Indirect call -> direct call from other "
>>>> +          "module %T=> %i (will resolve only with LTO)\n",
>>>> +          gimple_call_fn (stmt), (int) val);
>>>> +        }
>>>> +      continue;
>>>> +    }
>>>> +
>>>> +      if (!check_ic_target (stmt, direct_call))
>>>>        {
>>>>          if (dump_enabled_p ())
>>>> -        dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
>>>> -                 "Indirect call -> direct call from other "
>>>> -                 "module %T=> %i (will resolve only with LTO)\n",
>>>> -                 gimple_call_fn (stmt), (int)val);
>>>> +        dump_printf_loc (
>>>> +          MSG_MISSED_OPTIMIZATION, stmt,
>>>> +          "Indirect call -> direct call %T => %T "
>>>> +          "transformation skipped because of type mismatch: %G",
>>>> +          gimple_call_fn (stmt), direct_call->decl, stmt);
>>>> +      gimple_remove_histogram_value (cfun, stmt, histogram);
>>>> +      return false;
>>>>        }
>>>> -      return false;
>>>> -    }
>>>>    -  if (!check_ic_target (stmt, direct_call))
>>>> -    {
>>>>          if (dump_enabled_p ())
>>>> -    dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
>>>> -             "Indirect call -> direct call %T => %T "
>>>> -             "transformation skipped because of type mismatch: %G",
>>>> -             gimple_call_fn (stmt), direct_call->decl, stmt);
>>>> -      gimple_remove_histogram_value (cfun, stmt, histogram);
>>>> -      return false;
>>>> -    }
>>>> -
>>>> -  if (dump_enabled_p ())
>>>> -    {
>>>> -      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
>>>> -               "Indirect call -> direct call "
>>>> -               "%T => %T transformation on insn postponed\n",
>>>> -               gimple_call_fn (stmt), direct_call->decl);
>>>> -      dump_printf_loc (MSG_NOTE, stmt,
>>>> -               "hist->count %" PRId64
>>>> -               " hist->all %" PRId64"\n", count, all);
>>>> +    {
>>>> +      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
>>>> +               "Indirect call -> direct call "
>>>> +               "%T => %T transformation on insn postponed\n",
>>>> +               gimple_call_fn (stmt), direct_call->decl);
>>>> +      dump_printf_loc (MSG_NOTE, stmt,
>>>> +               "hist->count %" PRId64 " hist->all %" PRId64 "\n",
>>>> +               count, all);
>>>> +    }
>>>>        }
>>>>        return true;
>>>>
>>>
> 

[-- Attachment #2: v5-0001-Missed-function-specialization-partial-devirtuali.patch --]
[-- Type: text/plain, Size: 42045 bytes --]

From 350ddf812548d4dc44fb1175851733a929cfa739 Mon Sep 17 00:00:00 2001
From: Xiong Hu Luo <luoxhu@linux.ibm.com>
Date: Wed, 24 Apr 2019 00:10:44 -0500
Subject: [PATCH v5] Missed function specialization + partial devirtualization

This patch aims to fix PR69678 caused by PGO indirect call profiling
performance issues.
The bug that profiling data is never working was fixed by Martin's pull
back of topN patches, performance got GEOMEAN ~1% improvement(+24% for
511.povray_r specifically).
Still, currently the default profile only generates SINGLE indirect target
that called more than 75%.  This patch leverages MULTIPLE indirect
targets use in LTO-WPA and LTO-LTRANS stage, as a result, function
specialization, profiling, partial devirtualization, inlining and
cloning could be done successfully based on it.
Performance can get improved from 0.70 sec to 0.38 sec on simple tests.
Details are:
  1.  PGO with topn is enabled by default now, but only one indirect
  target edge will be generated in ipa-profile pass, so add variables to enable
  multiple speculative edges through passes, speculative_id will record the
  direct edge index bind to the indirect edge, indirect_call_targets length
  records how many direct edges owned by the indirect edge, postpone gimple_ic
  to ipa-profile like default as inline pass will decide whether it is benefit
  to transform indirect call.
  2.  Use speculative_id to track and search the reference node matched
  with the direct edge's callee for multiple targets.  Actually, it is the
  caller's responsibility to handle the direct edges mapped to same indirect
  edge.  speculative_call_info will return one of the direct edge specified,
  this will leverage current IPA edge process framework mostly.
  3.  Enable LTO WPA/LTRANS stage multiple indirect call targets analysis for
  profile full support in ipa passes and cgraph_edge functions.  speculative_id
  can be set by make_speculative id when multiple targets are binded to
  one indirect edge, and cloned if new edge is cloned.  speculative_id
  is streamed out and stream int by lto like lto_stmt_uid.
  4.  Add 1 in module testcase and 2 cross module testcases.
  5.  Bootstrap and regression test passed on Power8-LE.  No function
  and performance regression for SPEC2017.

gcc/ChangeLog

	2019-09-27  Xiong Hu Luo  <luoxhu@linux.ibm.com>

	PR ipa/69678
	* cgraph.c (symbol_table::create_edge): Init speculative_id.
	(cgraph_edge::make_speculative): Add param for setting speculative_id.
	(cgraph_edge::speculative_call_info): Find reference by
	speculative_id for multiple indirect targets.
	(cgraph_edge::resolve_speculation): Decrease the speculations
	for indirect edge, drop it's speculative if not direct target
	left.
	(cgraph_edge::redirect_call_stmt_to_callee): Likewise.
	(cgraph_node::verify_node): Don't report error if speculative
	edge not include statement.
	(cgraph_edge::has_multiple_indirect_call_p): New function.
	(cgraph_edge::has_indirect_call_p): New function.
	* cgraph.h (struct indirect_target_info): New struct.
	(indirect_call_targets): New vector variable.
	(make_speculative): Add param for setting speculative_id.
	(cgraph_edge::has_multiple_indirect_call_p): New declare.
	(cgraph_edge::has_indirect_call_p): New declare.
	(speculative_id): New variable.
	* cgraphclones.c (cgraph_node::create_clone): Clone speculative_id.
	* ipa-inline.c (inline_small_functions): Fix iterator update.
	* ipa-profile.c (ipa_profile_generate_summary): Add indirect
	multiple targets logic.
	(ipa_profile): Likewise.
	* ipa-ref.h (speculative_id): New variable.
	* ipa.c (process_references): Fix typo.
	* lto-cgraph.c (lto_output_edge): Add indirect multiple targets
	logic.  Stream out speculative_id.
	(input_edge): Likewise.
	* predict.c (dump_prediction): Revome edges count assert to be
	precise.
	* symtab.c (symtab_node::create_reference): Init speculative_id.
	(symtab_node::clone_references): Clone speculative_id.
	(symtab_node::clone_referring): Clone speculative_id.
	(symtab_node::clone_reference): Clone speculative_id.
	(symtab_node::clear_stmts_in_references): Clear speculative_id.
	* tree-inline.c (copy_bb): Duplicate all the speculative edges
	if indirect call contains multiple speculative targets.
	* tree-profile.c (gimple_gen_ic_profiler): Use the new variable
	__gcov_indirect_call.counters and __gcov_indirect_call.callee.
	(gimple_gen_ic_func_profiler): Likewise.
	(pass_ipa_tree_profile::gate): Fix comment typos.
	* value-prof.c  (gimple_ic_transform): Handle topn case.
	Fix comment typos.

gcc/testsuite/ChangeLog

	2019-09-27  Xiong Hu Luo  <luoxhu@linux.ibm.com>

	PR ipa/69678
	* gcc.dg/tree-prof/indir-call-prof-topn.c: New testcase.
	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c: New testcase.
	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c: New testcase.
	* gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c: New testcase.
	* lib/scandump.exp: Dump executable file name.
	* lib/scanwpaipa.exp: New scan-pgo-wap-ipa-dump.
---
 gcc/cgraph.c                                  | 90 ++++++++++++++++-
 gcc/cgraph.h                                  | 34 ++++++-
 gcc/cgraphclones.c                            |  1 +
 gcc/ipa-inline.c                              | 15 +--
 gcc/ipa-profile.c                             | 97 +++++++++++++------
 gcc/ipa-ref.h                                 |  1 +
 gcc/ipa.c                                     |  2 +-
 gcc/lto-cgraph.c                              | 56 +++++++++--
 gcc/predict.c                                 |  1 -
 gcc/symtab.c                                  |  5 +
 .../tree-prof/crossmodule-indir-call-topn-1.c | 33 +++++++
 .../crossmodule-indir-call-topn-1a.c          | 22 +++++
 .../tree-prof/crossmodule-indir-call-topn-2.c | 40 ++++++++
 .../gcc.dg/tree-prof/indir-call-prof-topn.c   | 37 +++++++
 gcc/testsuite/lib/scandump.exp                |  1 +
 gcc/testsuite/lib/scanwpaipa.exp              | 23 +++++
 gcc/tree-inline.c                             | 19 ++++
 gcc/tree-profile.c                            | 12 +--
 gcc/value-prof.c                              | 88 +++++++++--------
 19 files changed, 476 insertions(+), 101 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c

diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index 331b363c175..8ede0e4df21 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -860,6 +860,7 @@ symbol_table::create_edge (cgraph_node *caller, cgraph_node *callee,
   edge->prev_callee = NULL;
   edge->next_callee = NULL;
   edge->lto_stmt_uid = 0;
+  edge->speculative_id = 0;
 
   edge->count = count;
 
@@ -1051,7 +1052,8 @@ cgraph_edge::remove (void)
    Return direct edge created.  */
 
 cgraph_edge *
-cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
+cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count,
+			       unsigned int speculative_id)
 {
   cgraph_node *n = caller;
   ipa_ref *ref = NULL;
@@ -1069,11 +1071,13 @@ cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
   else
     e2->can_throw_external = can_throw_external;
   e2->lto_stmt_uid = lto_stmt_uid;
+  e2->speculative_id = speculative_id;
   e2->in_polymorphic_cdtor = in_polymorphic_cdtor;
   count -= e2->count;
   symtab->call_edge_duplication_hooks (this, e2);
   ref = n->create_reference (n2, IPA_REF_ADDR, call_stmt);
   ref->lto_stmt_uid = lto_stmt_uid;
+  ref->speculative_id = speculative_id;
   ref->speculative = speculative;
   n2->mark_address_taken ();
   return e2;
@@ -1087,6 +1091,38 @@ cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
    call) and if one of them exists, all of them must exist.
 
    Given speculative call edge, return all three components.
+
+   For some indirect edge, it may maps to multiple direct edges, i.e.  1:N.
+   check the speculative_id to return all the three components for specified
+   direct edge or indirect edge.
+   If input is indirect, caller of this function will get the direct edge one by
+   one, get_edge will just return one of the direct edge mapped to the indirect
+   edge, the returned direct edge will be resolved or redirected by the caller,
+   then number of indirect calls (speculations) is deceased in each access.
+   If input is direct, this function will get the indirect edge and reference
+   with matched speculative_id, the returned edge will also be resolved or
+   redirected, decrease the speculations accordingly.
+   Speculations of indirect edge will be dropped only if all direct edges
+   be handled.
+
+   e.g.  for indirect edge E statement "call call_dest":
+
+   Redirect N3 after redirected N2:
+
+   if (call_dest == N2)
+     n2 ();
+   else if (call_dest == N3)
+     n3 ();
+   else
+     call call_dest
+
+   Resolve N3 and only redirect N2:
+
+   if (call_dest == N2)
+     n2 ();
+   else
+     call call_dest
+
  */
 
 void
@@ -1126,7 +1162,7 @@ cgraph_edge::speculative_call_info (cgraph_edge *&direct,
 
   reference = NULL;
   for (i = 0; e->caller->iterate_reference (i, ref); i++)
-    if (ref->speculative
+    if (ref->speculative && ref->speculative_id == e->speculative_id
 	&& ((ref->stmt && ref->stmt == e->call_stmt)
 	    || (!ref->stmt && ref->lto_stmt_uid == e->lto_stmt_uid)))
       {
@@ -1187,7 +1223,21 @@ cgraph_edge::resolve_speculation (tree callee_decl)
          in the functions inlined through it.  */
     }
   edge->count += e2->count;
-  edge->speculative = false;
+  /* edge is indirect, e2 is direct.  If edge contains multiple speculations,
+     remove one of speculations for this indirect edge, then if edge still
+     contains direct target, keep the speculation, next direct target
+     will continue use it.  Give up speculation completely if no direct
+     target is left for this indirect edge.  */
+  if (edge->has_indirect_call_p ())
+    {
+      /* As the direct targets are sorted by decrease, delete the first target
+	 when it is resolved.  */
+      edge->indirect_info->indirect_call_targets->ordered_remove (0);
+      if (edge->indirect_info->indirect_call_targets->is_empty ())
+	edge->speculative = false;
+    }
+  else
+    edge->speculative = false;
   e2->speculative = false;
   ref->remove_reference ();
   if (e2->indirect_unknown_callee || e2->inline_failed)
@@ -1321,7 +1371,21 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
 	  e->caller->set_call_stmt_including_clones (e->call_stmt, new_stmt,
 						     false);
 	  e->count = gimple_bb (e->call_stmt)->count;
-	  e2->speculative = false;
+	  /* edge is direct, e2 is indirect here.  If e2 contains multiple
+	     speculations, remove one of speculations for this indirect edge,
+	     then if e2 still contains direct target, keep the speculation,
+	     next direct target will continue use it.  Give up speculation
+	     completely if no direct target is left for this indirect e2.  */
+	  if (e2->has_indirect_call_p ())
+	    {
+	      /* As the direct targets are sorted by decrease, delete the first
+		 target when it is redirected.  */
+	      e2->indirect_info->indirect_call_targets->ordered_remove (0);
+	      if (e2->indirect_info->indirect_call_targets->is_empty ())
+		e2->speculative = false;
+	    }
+	  else
+	    e2->speculative = false;
 	  e2->count = gimple_bb (e2->call_stmt)->count;
 	  ref->speculative = false;
 	  ref->stmt = NULL;
@@ -3376,7 +3440,7 @@ cgraph_node::verify_node (void)
 
       for (e = callees; e; e = e->next_callee)
 	{
-	  if (!e->aux)
+	  if (!e->aux && !e->speculative)
 	    {
 	      error ("edge %s->%s has no corresponding call_stmt",
 		     identifier_to_locale (e->caller->name ()),
@@ -3803,6 +3867,22 @@ cgraph_edge::possibly_call_in_translation_unit_p (void)
   return node->get_availability () >= AVAIL_AVAILABLE;
 }
 
+/* Return true if this edge has multiple indirect call targets.  */
+bool
+cgraph_edge::has_multiple_indirect_call_p (void)
+{
+  return (indirect_info && indirect_info->indirect_call_targets
+	  && indirect_info->indirect_call_targets->length () > 1);
+}
+
+/* Return true if this edge has at least one indirect call target.  */
+bool
+cgraph_edge::has_indirect_call_p (void)
+{
+  return (indirect_info && indirect_info->indirect_call_targets
+	  && !indirect_info->indirect_call_targets->is_empty ());
+}
+
 /* A stashed copy of "symtab" for use by selftest::symbol_table_test.
    This needs to be a global so that it can be a GC root, and thus
    prevent the stashed copy from being garbage-collected if the GC runs
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 1da6cab54b0..f67ef0d7f72 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -1647,6 +1647,21 @@ private:
   void make_speculative (tree otr_type = NULL);
 };
 
+/* Structure containing indirect target information from profile.  */
+
+struct GTY (()) indirect_target_info
+{
+  indirect_target_info (unsigned int id, int prob)
+    : common_target_id (id), common_target_probability (prob)
+  {
+  }
+
+  /* Profile_id of common target obtained from profile.  */
+  unsigned int common_target_id;
+  /* Probability that call will land in function with COMMON_TARGET_ID.  */
+  int common_target_probability;
+};
+
 /* Structure containing additional information about an indirect call.  */
 
 class GTY(()) cgraph_indirect_call_info
@@ -1665,10 +1680,9 @@ public:
   int param_index;
   /* ECF flags determined from the caller.  */
   int ecf_flags;
-  /* Profile_id of common target obtrained from profile.  */
-  int common_target_id;
-  /* Probability that call will land in function with COMMON_TARGET_ID.  */
-  int common_target_probability;
+
+  /* An indirect call may contain one or multiple call targets.  */
+  vec<indirect_target_info, va_gc> *indirect_call_targets;
 
   /* Set when the call is a virtual call with the parameter being the
      associated object pointer rather than a simple direct call.  */
@@ -1725,7 +1739,8 @@ public:
   /* Turn edge into speculative call calling N2. Update
      the profile so the direct call is taken COUNT times
      with FREQUENCY.  */
-  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count);
+  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count,
+				 unsigned int speculative_id = 0);
 
    /* Given speculative call edge, return all three components.  */
   void speculative_call_info (cgraph_edge *&direct, cgraph_edge *&indirect,
@@ -1784,6 +1799,12 @@ public:
      be internal to the current translation unit.  */
   bool possibly_call_in_translation_unit_p (void);
 
+  /* Return true if this edge has multiple indirect call targets.  */
+  bool has_multiple_indirect_call_p (void);
+
+  /* Return true if this edge has at least one indirect call target.  */
+  bool has_indirect_call_p (void);
+
   /* Expected number of executions: calculated in profile.c.  */
   profile_count count;
   cgraph_node *caller;
@@ -1803,6 +1824,9 @@ public:
   /* The stmt_uid of call_stmt.  This is used by LTO to recover the call_stmt
      when the function is serialized in.  */
   unsigned int lto_stmt_uid;
+  /* speculative id is used by multiple indirect targets when the function is
+   speculated.  */
+  unsigned int speculative_id;
   /* Whether this edge was made direct by indirect inlining.  */
   unsigned int indirect_inlining_edge : 1;
   /* Whether this edge describes an indirect call with an undetermined
diff --git a/gcc/cgraphclones.c b/gcc/cgraphclones.c
index 909407b9a71..0c9160d9240 100644
--- a/gcc/cgraphclones.c
+++ b/gcc/cgraphclones.c
@@ -128,6 +128,7 @@ cgraph_edge::clone (cgraph_node *n, gcall *call_stmt, unsigned stmt_uid,
   new_edge->inline_failed = inline_failed;
   new_edge->indirect_inlining_edge = indirect_inlining_edge;
   new_edge->lto_stmt_uid = stmt_uid;
+  new_edge->speculative_id = speculative_id;
   /* Clone flags that depend on call_stmt availability manually.  */
   new_edge->can_throw_external = can_throw_external;
   new_edge->call_stmt_cannot_inline_p = call_stmt_cannot_inline_p;
diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
index b62d280eb25..6136214f9ac 100644
--- a/gcc/ipa-inline.c
+++ b/gcc/ipa-inline.c
@@ -1881,12 +1881,15 @@ inline_small_functions (void)
 	}
       if (has_speculative)
 	for (edge = node->callees; edge; edge = next)
-	  if (edge->speculative && !speculation_useful_p (edge,
-							  edge->aux != NULL))
-	    {
-	      edge->resolve_speculation ();
-	      update = true;
-	    }
+	  {
+	    next = edge->next_callee;
+	    if (edge->speculative
+		&& !speculation_useful_p (edge, edge->aux != NULL))
+	      {
+		edge->resolve_speculation ();
+		update = true;
+	      }
+	  }
       if (update)
 	{
 	  struct cgraph_node *where = node->global.inlined_to
diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
index 970dba39c80..13bd05a1061 100644
--- a/gcc/ipa-profile.c
+++ b/gcc/ipa-profile.c
@@ -192,23 +192,34 @@ ipa_profile_generate_summary (void)
 		  if (h)
 		    {
 		      gcov_type val, count, all;
-		      if (get_nth_most_common_value (NULL, "indirect call", h,
-						     &val, &count, &all))
+		      struct cgraph_edge *e = node->get_edge (stmt);
+		      if (e && !e->indirect_unknown_callee)
+			continue;
+
+		      for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
 			{
-			  struct cgraph_edge * e = node->get_edge (stmt);
-			  if (e && !e->indirect_unknown_callee)
+			  if (!get_nth_most_common_value (NULL, "indirect call",
+							  h, &val, &count, &all,
+							  j))
+			    continue;
+
+			  if (val == 0)
 			    continue;
 
-			  e->indirect_info->common_target_id = val;
-			  e->indirect_info->common_target_probability
-			    = GCOV_COMPUTE_SCALE (count, all);
-			  if (e->indirect_info->common_target_probability > REG_BR_PROB_BASE)
+			  indirect_target_info item (val,
+						     GCOV_COMPUTE_SCALE (count,
+									 all));
+			  if (item.common_target_probability > REG_BR_PROB_BASE)
 			    {
 			      if (dump_file)
-				fprintf (dump_file, "Probability capped to 1\n");
-			      e->indirect_info->common_target_probability = REG_BR_PROB_BASE;
+				fprintf (dump_file,
+					 "Probability capped to 1\n");
+			      item.common_target_probability = REG_BR_PROB_BASE;
 			    }
+			  vec_safe_push (
+			    e->indirect_info->indirect_call_targets, item);
 			}
+
 		      gimple_remove_histogram_value (DECL_STRUCT_FUNCTION (node->decl),
 						      stmt, h);
 		    }
@@ -492,6 +503,7 @@ ipa_profile (void)
   int nindirect = 0, ncommon = 0, nunknown = 0, nuseless = 0, nconverted = 0;
   int nmismatch = 0, nimpossible = 0;
   bool node_map_initialized = false;
+  gcov_type threshold;
 
   if (dump_file)
     dump_histogram (dump_file, histogram);
@@ -500,14 +512,12 @@ ipa_profile (void)
       overall_time += histogram[i]->count * histogram[i]->time;
       overall_size += histogram[i]->size;
     }
+  threshold = 0;
   if (overall_time)
     {
-      gcov_type threshold;
-
       gcc_assert (overall_size);
 
       cutoff = (overall_time * PARAM_VALUE (HOT_BB_COUNT_WS_PERMILLE) + 500) / 1000;
-      threshold = 0;
       for (i = 0; cumulated < cutoff; i++)
 	{
 	  cumulated += histogram[i]->count * histogram[i]->time;
@@ -543,7 +553,7 @@ ipa_profile (void)
   histogram.release ();
   histogram_pool.release ();
 
-  /* Produce speculative calls: we saved common traget from porfiling into
+  /* Produce speculative calls: we saved common target from profiling into
      e->common_target_id.  Now, at link time, we can look up corresponding
      function node and produce speculative call.  */
 
@@ -558,13 +568,37 @@ ipa_profile (void)
 	{
 	  if (n->count.initialized_p ())
 	    nindirect++;
-	  if (e->indirect_info->common_target_id)
+	  if (e->has_indirect_call_p ())
 	    {
 	      if (!node_map_initialized)
-	        init_node_map (false);
+		init_node_map (false);
 	      node_map_initialized = true;
 	      ncommon++;
-	      n2 = find_func_by_profile_id (e->indirect_info->common_target_id);
+
+	      if (in_lto_p)
+		{
+		  if (dump_file)
+		    {
+		      fprintf (dump_file,
+			       "Updating hotness threshold in LTO mode.\n");
+		      fprintf (dump_file, "Updated min count: %" PRId64 "\n",
+			       (int64_t) threshold);
+		    }
+		  set_hot_bb_threshold (threshold
+		    / e->indirect_info->indirect_call_targets->length ());
+		}
+
+	      unsigned speculative_id = 0;
+	      indirect_target_info *item;
+	      /* The code below is not formatted yet for review convenience.
+		 Move to a seprate small function is not easy as too many local
+		 variables used in it.  Need format and remove this comments
+		 once got approved.  */
+	      FOR_EACH_VEC_SAFE_ELT (e->indirect_info->indirect_call_targets, i,
+				     item)
+	       {
+	      bool speculative_found = false;
+	      n2 = find_func_by_profile_id (item->common_target_id);
 	      if (n2)
 		{
 		  if (dump_file)
@@ -573,11 +607,10 @@ ipa_profile (void)
 			       " other module %s => %s, prob %3.2f\n",
 			       n->dump_name (),
 			       n2->dump_name (),
-			       e->indirect_info->common_target_probability
-			       / (float)REG_BR_PROB_BASE);
+			       item->common_target_probability
+				 / (float) REG_BR_PROB_BASE);
 		    }
-		  if (e->indirect_info->common_target_probability
-		      < REG_BR_PROB_BASE / 2)
+		  if (item->common_target_probability < REG_BR_PROB_BASE / 2)
 		    {
 		      nuseless++;
 		      if (dump_file)
@@ -613,7 +646,7 @@ ipa_profile (void)
 		      if (dump_file)
 			fprintf (dump_file,
 				 "Not speculating: "
-				 "parameter count mistmatch\n");
+				 "parameter count mismatch\n");
 		    }
 		  else if (e->indirect_info->polymorphic
 			   && !opt_for_fn (n->decl, flag_devirtualize)
@@ -640,20 +673,30 @@ ipa_profile (void)
 			    n2 = alias;
 			}
 		      nconverted++;
-		      e->make_speculative
-			(n2,
-			 e->count.apply_probability
-				     (e->indirect_info->common_target_probability));
+		      e->make_speculative (n2,
+					   e->count.apply_probability (
+					     item->common_target_probability),
+					   speculative_id);
 		      update = true;
+		      speculative_id++;
+		      speculative_found = true;
 		    }
 		}
 	      else
 		{
 		  if (dump_file)
 		    fprintf (dump_file, "Function with profile-id %i not found.\n",
-			     e->indirect_info->common_target_id);
+			     item->common_target_id);
 		  nunknown++;
 		}
+	      if (!speculative_found)
+		{
+		  /* Remove item from indirect_call_targets if no
+		     speculative edge generated, rollback the iteration.  */
+		  e->indirect_info->indirect_call_targets->ordered_remove (i);
+		  i--;
+		}
+	       }
 	    }
 	 }
        if (update)
diff --git a/gcc/ipa-ref.h b/gcc/ipa-ref.h
index 0d8e509c932..3e6562ec9d1 100644
--- a/gcc/ipa-ref.h
+++ b/gcc/ipa-ref.h
@@ -59,6 +59,7 @@ public:
   symtab_node *referred;
   gimple *stmt;
   unsigned int lto_stmt_uid;
+  unsigned int speculative_id;
   unsigned int referred_index;
   ENUM_BITFIELD (ipa_ref_use) use:3;
   unsigned int speculative:1;
diff --git a/gcc/ipa.c b/gcc/ipa.c
index 6b84e1f9bda..a10b0603f14 100644
--- a/gcc/ipa.c
+++ b/gcc/ipa.c
@@ -166,7 +166,7 @@ process_references (symtab_node *snode,
    devirtualization happens.  After inlining still keep their declarations
    around, so we can devirtualize to a direct call.
 
-   Also try to make trivial devirutalization when no or only one target is
+   Also try to make trivial devirtualization when no or only one target is
    possible.  */
 
 static void
diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
index 147975ba869..047dbd20df0 100644
--- a/gcc/lto-cgraph.c
+++ b/gcc/lto-cgraph.c
@@ -238,6 +238,7 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
   unsigned int uid;
   intptr_t ref;
   struct bitpack_d bp;
+  unsigned len;
 
   if (edge->indirect_unknown_callee)
     streamer_write_enum (ob->main_stream, LTO_symtab_tags, LTO_symtab_last_tag,
@@ -265,6 +266,7 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
   bp_pack_enum (&bp, cgraph_inline_failed_t,
 	        CIF_N_REASONS, edge->inline_failed);
   bp_pack_var_len_unsigned (&bp, uid);
+  bp_pack_var_len_unsigned (&bp, edge->speculative_id);
   bp_pack_value (&bp, edge->indirect_inlining_edge, 1);
   bp_pack_value (&bp, edge->speculative, 1);
   bp_pack_value (&bp, edge->call_stmt_cannot_inline_p, 1);
@@ -291,11 +293,27 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
   streamer_write_bitpack (&bp);
   if (edge->indirect_unknown_callee)
     {
-      streamer_write_hwi_stream (ob->main_stream,
-			         edge->indirect_info->common_target_id);
-      if (edge->indirect_info->common_target_id)
-	streamer_write_hwi_stream
-	   (ob->main_stream, edge->indirect_info->common_target_probability);
+      indirect_target_info *item;
+      unsigned int i;
+      len = edge->has_indirect_call_p ()
+	      ? edge->indirect_info->indirect_call_targets->length ()
+	      : 0;
+      gcc_assert (len <= GCOV_TOPN_VALUES);
+
+      streamer_write_hwi_stream (ob->main_stream, len);
+
+      if (len)
+	{
+	  FOR_EACH_VEC_SAFE_ELT (edge->indirect_info->indirect_call_targets, i,
+				 item)
+	    {
+	      gcc_assert (item->common_target_id);
+	      streamer_write_hwi_stream (ob->main_stream,
+					 item->common_target_id);
+	      streamer_write_hwi_stream (ob->main_stream,
+					 item->common_target_probability);
+	    }
+	}
     }
 }
 
@@ -688,6 +706,7 @@ lto_output_ref (struct lto_simple_output_block *ob, struct ipa_ref *ref,
       if (ref->stmt)
 	uid = gimple_uid (ref->stmt) + 1;
       streamer_write_hwi_stream (ob->main_stream, uid);
+      streamer_write_hwi_stream (ob->main_stream, ref->speculative_id);
     }
 }
 
@@ -1419,7 +1438,10 @@ input_ref (class lto_input_block *ib,
   ref = referring_node->create_reference (node, use);
   ref->speculative = speculative;
   if (is_a <cgraph_node *> (referring_node))
-    ref->lto_stmt_uid = streamer_read_hwi (ib);
+    {
+      ref->lto_stmt_uid = streamer_read_hwi (ib);
+      ref->speculative_id = streamer_read_hwi (ib);
+    }
 }
 
 /* Read an edge from IB.  NODES points to a vector of previously read nodes for
@@ -1433,11 +1455,12 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
 {
   struct cgraph_node *caller, *callee;
   struct cgraph_edge *edge;
-  unsigned int stmt_id;
+  unsigned int stmt_id, speculative_id;
   profile_count count;
   cgraph_inline_failed_t inline_failed;
   struct bitpack_d bp;
   int ecf_flags = 0;
+  unsigned i, len;
 
   caller = dyn_cast<cgraph_node *> (nodes[streamer_read_hwi (ib)]);
   if (caller == NULL || caller->decl == NULL_TREE)
@@ -1457,6 +1480,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
   bp = streamer_read_bitpack (ib);
   inline_failed = bp_unpack_enum (&bp, cgraph_inline_failed_t, CIF_N_REASONS);
   stmt_id = bp_unpack_var_len_unsigned (&bp);
+  speculative_id = bp_unpack_var_len_unsigned (&bp);
 
   if (indirect)
     edge = caller->create_indirect_edge (NULL, 0, count);
@@ -1466,6 +1490,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
   edge->indirect_inlining_edge = bp_unpack_value (&bp, 1);
   edge->speculative = bp_unpack_value (&bp, 1);
   edge->lto_stmt_uid = stmt_id;
+  edge->speculative_id = speculative_id;
   edge->inline_failed = inline_failed;
   edge->call_stmt_cannot_inline_p = bp_unpack_value (&bp, 1);
   edge->can_throw_external = bp_unpack_value (&bp, 1);
@@ -1485,9 +1510,20 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
       if (bp_unpack_value (&bp, 1))
 	ecf_flags |= ECF_RETURNS_TWICE;
       edge->indirect_info->ecf_flags = ecf_flags;
-      edge->indirect_info->common_target_id = streamer_read_hwi (ib);
-      if (edge->indirect_info->common_target_id)
-        edge->indirect_info->common_target_probability = streamer_read_hwi (ib);
+
+      len = streamer_read_hwi (ib);
+
+      gcc_assert (len <= GCOV_TOPN_VALUES);
+
+      if (len)
+	{
+	  for (i = 0; i < len; i++)
+	    {
+	      indirect_target_info item (streamer_read_hwi (ib),
+					 streamer_read_hwi (ib));
+	      vec_safe_push (edge->indirect_info->indirect_call_targets, item);
+	    }
+	}
     }
 }
 
diff --git a/gcc/predict.c b/gcc/predict.c
index 915f0806b11..3f56fa3a74a 100644
--- a/gcc/predict.c
+++ b/gcc/predict.c
@@ -762,7 +762,6 @@ dump_prediction (FILE *file, enum br_predictor predictor, int probability,
       && bb->count.precise_p ()
       && reason == REASON_NONE)
     {
-      gcc_assert (e->count ().precise_p ());
       fprintf (file, ";;heuristics;%s;%" PRId64 ";%" PRId64 ";%.1f;\n",
 	       predictor_info[predictor].name,
 	       bb->count.to_gcov_type (), e->count ().to_gcov_type (),
diff --git a/gcc/symtab.c b/gcc/symtab.c
index ee9723c3453..d4c36fd3e5a 100644
--- a/gcc/symtab.c
+++ b/gcc/symtab.c
@@ -603,6 +603,7 @@ symtab_node::create_reference (symtab_node *referred_node,
   ref->referred = referred_node;
   ref->stmt = stmt;
   ref->lto_stmt_uid = 0;
+  ref->speculative_id = 0;
   ref->use = use_type;
   ref->speculative = 0;
 
@@ -660,6 +661,7 @@ symtab_node::clone_references (symtab_node *node)
       ref2 = create_reference (ref->referred, ref->use, ref->stmt);
       ref2->speculative = speculative;
       ref2->lto_stmt_uid = stmt_uid;
+      ref2->speculative_id = ref->speculative_id;
     }
 }
 
@@ -678,6 +680,7 @@ symtab_node::clone_referring (symtab_node *node)
       ref2 = ref->referring->create_reference (this, ref->use, ref->stmt);
       ref2->speculative = speculative;
       ref2->lto_stmt_uid = stmt_uid;
+      ref2->speculative_id = ref->speculative_id;
     }
 }
 
@@ -693,6 +696,7 @@ symtab_node::clone_reference (ipa_ref *ref, gimple *stmt)
   ref2 = create_reference (ref->referred, ref->use, stmt);
   ref2->speculative = speculative;
   ref2->lto_stmt_uid = stmt_uid;
+  ref2->speculative_id = ref->speculative_id;
   return ref2;
 }
 
@@ -747,6 +751,7 @@ symtab_node::clear_stmts_in_references (void)
       {
 	r->stmt = NULL;
 	r->lto_stmt_uid = 0;
+	r->speculative_id = 0;
       }
 }
 
diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
new file mode 100644
index 00000000000..a13b08cd60e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
@@ -0,0 +1,33 @@
+/* { dg-require-effective-target lto } */
+/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate" } */
+
+#include <stdio.h>
+
+typedef int (*fptr) (int);
+int
+one (int a);
+
+int
+two (int a);
+
+fptr table[] = {&one, &two};
+
+int
+main()
+{
+  int i, x;
+  fptr p = &one;
+
+  x = one (3);
+
+  for (i = 0; i < 350000000; i++)
+    {
+      x = (*p) (3);
+      p = table[x];
+    }
+  printf ("done:%d\n", x);
+}
+
+/* { dg-final-use-not-autofdo { scan-pgo-wpa-ipa-dump "2 \\(200.00%\\) speculations produced." "profile_estimate" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
new file mode 100644
index 00000000000..a8c6e365fb9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
@@ -0,0 +1,22 @@
+/* It seems there is no way to avoid the other source of mulitple
+   source testcase from being compiled independently.  Just avoid
+   error.  */
+#ifdef DOJOB
+int
+one (int a)
+{
+  return 1;
+}
+
+int
+two (int a)
+{
+  return 0;
+}
+#else
+int
+main()
+{
+  return 0;
+}
+#endif
diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
new file mode 100644
index 00000000000..9b996fcf0ed
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
@@ -0,0 +1,40 @@
+/* { dg-require-effective-target lto } */
+/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate" } */
+
+#include <stdio.h>
+
+typedef int (*fptr) (int);
+int
+one (int a);
+
+int
+two (int a);
+
+fptr table[] = {&one, &two};
+
+int foo ()
+{
+  int i, x;
+  fptr p = &one;
+
+  x = one (3);
+
+  for (i = 0; i < 350000000; i++)
+    {
+      x = (*p) (3);
+      p = table[x];
+    }
+  return x;
+}
+
+int
+main()
+{
+  int x = foo ();
+  printf ("done:%d\n", x);
+}
+
+/* { dg-final-use-not-autofdo { scan-pgo-wpa-ipa-dump "2 \\(200.00%\\) speculations produced." "profile_estimate" } } */
+
diff --git a/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
new file mode 100644
index 00000000000..063996c71df
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
@@ -0,0 +1,37 @@
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-options "-O2 -fdump-ipa-profile_estimate" } */
+
+#include <stdio.h>
+
+typedef int (*fptr) (int);
+int
+one (int a)
+{
+  return 1;
+}
+
+int
+two (int a)
+{
+  return 0;
+}
+
+fptr table[] = {&one, &two};
+
+int
+main()
+{
+  int i, x;
+  fptr p = &one;
+
+  one (3);
+
+  for (i = 0; i < 350000000; i++)
+    {
+      x = (*p) (3);
+      p = table[x];
+    }
+  printf ("done:%d\n", x);
+}
+
+/* { dg-final-use-not-autofdo { scan-ipa-dump "2 \\(200.00%\\) speculations produced." "profile_estimate" } } */
diff --git a/gcc/testsuite/lib/scandump.exp b/gcc/testsuite/lib/scandump.exp
index 42f5c01aa60..8b7cd7cfc16 100644
--- a/gcc/testsuite/lib/scandump.exp
+++ b/gcc/testsuite/lib/scandump.exp
@@ -70,6 +70,7 @@ proc scan-dump { args } {
     set output_file "[glob -nocomplain $dumpbase.[lindex $args 2]]"
     if { $output_file == "" } {
 	verbose -log "$testcase: dump file does not exist"
+	verbose -log "dump file: $dumpbase.$suf"
 	unresolved "$testname"
 	return
     }
diff --git a/gcc/testsuite/lib/scanwpaipa.exp b/gcc/testsuite/lib/scanwpaipa.exp
index b5549fd688e..8aafd6c82e8 100644
--- a/gcc/testsuite/lib/scanwpaipa.exp
+++ b/gcc/testsuite/lib/scanwpaipa.exp
@@ -45,6 +45,29 @@ proc scan-wpa-ipa-dump { args } {
     }
 }
 
+# Argument 0 is the regexp to match
+# Argument 1 is the name of the dumped ipa pass
+# Argument 2 handles expected failures and the like
+proc scan-pgo-wpa-ipa-dump { args } {
+
+    if { [llength $args] < 2 } {
+	error "scan-pgo-wpa-ipa-dump: too few arguments"
+	return
+    }
+    if { [llength $args] > 3 } {
+	error "scan-pgo-wpa-ipa-dump: too many arguments"
+	return
+    }
+    if { [llength $args] >= 3 } {
+	scan-dump "pgo-wpa-ipa" [lindex $args 0] \
+		  "\[0-9\]\[0-9\]\[0-9\]i.[lindex $args 1]" ".x02.wpa" \
+		  [lindex $args 2]
+    } else {
+	scan-dump "pgo-wpa-ipa" [lindex $args 0] \
+		  "\[0-9\]\[0-9\]\[0-9\]i.[lindex $args 1]" ".x02.wpa"
+    }
+}
+
 # Call pass if pattern is present given number of times, otherwise fail.
 # Argument 0 is the regexp to match
 # Argument 1 is number of times the regexp must be found
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index e4ae1b058fd..b3ec7f5b99b 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -2195,6 +2195,25 @@ copy_bb (copy_body_data *id, basic_block bb,
 
 			  gcc_assert (!edge->indirect_unknown_callee);
 			  old_edge->speculative_call_info (direct, indirect, ref);
+			  while (old_edge->next_callee
+				 && old_edge->next_callee->speculative
+				 && indirect->has_multiple_indirect_call_p ())
+			    {
+			      /* Some speculative calls may contain more than
+				 one direct target, loop iterate it to clone all
+				 related direct edges before cloning the related
+				 indirect edge.  */
+			      id->dst_node->clone_reference (ref, stmt);
+
+			      edge = old_edge->next_callee;
+			      edge = edge->clone (id->dst_node, call_stmt,
+						  gimple_uid (stmt), num, den,
+						  true);
+			      old_edge = old_edge->next_callee;
+			      gcc_assert (!edge->indirect_unknown_callee);
+			      old_edge->speculative_call_info (direct, indirect,
+							       ref);
+			    }
 
 			  profile_count indir_cnt = indirect->count;
 			  indirect = indirect->clone (id->dst_node, call_stmt,
diff --git a/gcc/tree-profile.c b/gcc/tree-profile.c
index 4c1ead5781f..ef7748668f8 100644
--- a/gcc/tree-profile.c
+++ b/gcc/tree-profile.c
@@ -74,8 +74,8 @@ static GTY(()) tree ic_tuple_callee_field;
 /* Do initialization work for the edge profiler.  */
 
 /* Add code:
-   __thread gcov*	__gcov_indirect_call_counters; // pointer to actual counter
-   __thread void*	__gcov_indirect_call_callee; // actual callee address
+   __thread gcov*	__gcov_indirect_call.counters; // pointer to actual counter
+   __thread void*	__gcov_indirect_call.callee; // actual callee address
    __thread int __gcov_function_counter; // time profiler function counter
 */
 static void
@@ -382,7 +382,7 @@ gimple_gen_ic_profiler (histogram_value value, unsigned tag)
       f_1 = foo;
       __gcov_indirect_call.counters = &__gcov4.main[0];
       PROF_9 = f_1;
-      __gcov_indirect_call_callee = PROF_9;
+      __gcov_indirect_call.callee = PROF_9;
       _4 = f_1 ();
    */
 
@@ -445,11 +445,11 @@ gimple_gen_ic_func_profiler (void)
 
   /* Insert code:
 
-     if (__gcov_indirect_call_callee != NULL)
+     if (__gcov_indirect_call.callee != NULL)
        __gcov_indirect_call_profiler_v3 (profile_id, &current_function_decl);
 
      The function __gcov_indirect_call_profiler_v3 is responsible for
-     resetting __gcov_indirect_call_callee to NULL.  */
+     resetting __gcov_indirect_call.callee to NULL.  */
 
   gimple_stmt_iterator gsi = gsi_start_bb (cond_bb);
   void0 = build_int_cst (ptr_type_node, 0);
@@ -891,7 +891,7 @@ pass_ipa_tree_profile::gate (function *)
 {
   /* When profile instrumentation, use or test coverage shall be performed.
      But for AutoFDO, this there is no instrumentation, thus this pass is
-     diabled.  */
+     disabled.  */
   return (!in_lto_p && !flag_auto_profile
 	  && (flag_branch_probabilities || flag_test_coverage
 	      || profile_arc_flag));
diff --git a/gcc/value-prof.c b/gcc/value-prof.c
index 55ea0973a03..5f095d848f0 100644
--- a/gcc/value-prof.c
+++ b/gcc/value-prof.c
@@ -1406,11 +1406,10 @@ gimple_ic (gcall *icall_stmt, struct cgraph_node *direct_call,
   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:
- */
+/* There maybe multiple indirect targets in histogram.  Check every
+   indirect/virtual call if callee function exists, if not exist, leave it to
+   LTO stage for later process.  Modify code of this indirect call to an if-else
+   structure in ipa-profile finally.  */
 
 static bool
 gimple_ic_transform (gimple_stmt_iterator *gsi)
@@ -1434,51 +1433,60 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
   if (!histogram)
     return false;
 
-  if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
-				  &count, &all))
-    return false;
+  count = 0;
+  all = histogram->hvalue.counters[0];
 
-  if (4 * count <= 3 * all)
-    return false;
+  for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
+    {
+      if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
+				      &count, &all, j))
+	return false;
 
-  direct_call = find_func_by_profile_id ((int)val);
+      /* Minimum probability.  should be higher than 25%.  */
+      if (4 * count <= all)
+	return false;
 
-  if (direct_call == NULL)
-    {
-      if (val)
+      direct_call = find_func_by_profile_id ((int) val);
+
+      if (direct_call == NULL)
+	{
+	  if (val)
+	    {
+	      if (dump_enabled_p ())
+		dump_printf_loc (
+		  MSG_MISSED_OPTIMIZATION, stmt,
+		  "Indirect call -> direct call from other "
+		  "module %T=> %i (will resolve only with LTO)\n",
+		  gimple_call_fn (stmt), (int) val);
+	    }
+	  return false;
+	}
+
+      if (!check_ic_target (stmt, direct_call))
 	{
 	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
-			     "Indirect call -> direct call from other "
-			     "module %T=> %i (will resolve only with LTO)\n",
-			     gimple_call_fn (stmt), (int)val);
+	    dump_printf_loc (
+	      MSG_MISSED_OPTIMIZATION, stmt,
+	      "Indirect call -> direct call %T => %T "
+	      "transformation skipped because of type mismatch: %G",
+	      gimple_call_fn (stmt), direct_call->decl, stmt);
+	  gimple_remove_histogram_value (cfun, stmt, histogram);
+	  return false;
 	}
-      return false;
-    }
 
-  if (!check_ic_target (stmt, direct_call))
-    {
       if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
-			 "Indirect call -> direct call %T => %T "
-			 "transformation skipped because of type mismatch: %G",
-			 gimple_call_fn (stmt), direct_call->decl, stmt);
-      gimple_remove_histogram_value (cfun, stmt, histogram);
-      return false;
-    }
-
-  if (dump_enabled_p ())
-    {
-      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
-		       "Indirect call -> direct call "
-		       "%T => %T transformation on insn postponed\n",
-		       gimple_call_fn (stmt), direct_call->decl);
-      dump_printf_loc (MSG_NOTE, stmt,
-		       "hist->count %" PRId64
-		       " hist->all %" PRId64"\n", count, all);
+	{
+	  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
+			   "Indirect call -> direct call "
+			   "%T => %T transformation on insn postponed\n",
+			   gimple_call_fn (stmt), direct_call->decl);
+	  dump_printf_loc (MSG_NOTE, stmt,
+			   "hist->count %" PRId64 " hist->all %" PRId64 "\n",
+			   count, all);
+	}
     }
 
-  return true;
+  return false;
 }
 
 /* Return true if the stringop CALL shall be profiled.  SIZE_ARG be
-- 
2.21.0.777.g83232e3864


^ permalink raw reply	[flat|nested] 38+ messages in thread

* Ping: [PATCH v5] Missed function specialization + partial devirtualization
  2019-09-27  7:13               ` [PATCH v5] " luoxhu
@ 2019-10-16  3:33                 ` luoxhu
  2019-10-22 14:24                 ` Martin Liška
  1 sibling, 0 replies; 38+ messages in thread
From: luoxhu @ 2019-10-16  3:33 UTC (permalink / raw)
  To: Martin Liška, gcc-patches; +Cc: hubicka, segher, wschmidt

Ping:

Attachment: v5-0001-Missed-function-specialization-partial-devirtuali.patch:

https://gcc.gnu.org/ml/gcc-patches/2019-09/txtuTT17jV7n5.txt


Thanks,
Xiong Hu


On 2019/9/27 15:13, luoxhu wrote:
> Hi Martin,
> 
> Thanks for your time of so many round of reviews.
> It really helped me a lot.
> Updated with your comments and attached for Honza's review and approve.  :)
> 
> 
> Xiong Hu
> BR
> 
> On 2019/9/26 16:36, Martin Liška wrote:
>> On 9/26/19 7:23 AM, luoxhu wrote:
>>> Thanks Martin,
>>>
>>>
>>> On 2019/9/25 18:57, Martin Liška wrote:
>>>> On 9/25/19 5:45 AM, luoxhu wrote:
>>>>> Hi,
>>>>>
>>>>> Sorry for replying so late due to cauldron conference and other LTO 
>>>>> issues
>>>>> I was working on.
>>>>
>>>> Hello.
>>>>
>>>> That's fine, we still have plenty of time for patch review.
>>>>
>>>> Not fixed issues which I reported in v3 (and still valid in v4):
>>>> - please come up with indirect_target_info::indirect_target_info and 
>>>> use it
>>> Sorry for miss out.
>>
>> Hello.
>>
>> Sure, please use a contructor initialization (see my patch).
>>
>>>
>>>
>>>> - do you need to stream out indirect_call_targets when common_target_id 
>>>> == 0?
>>>
>>> No need to stream out items with common_target_id == 0, removed the if 
>>> condition in lto-cgraph.c.
>>
>> Fine. Do we have a guarantee that item->common_target_id is always != 0? 
>> Please put there an assert.
>>
>>>
>>>>
>>>> Then I'm suggesting to use vec::is_empty (please see my patch).
>>> OK.  But has_multiple_indirect_call_p should return different than 
>>> has_indirect_call_p as it checks more that one targets?
>>
>> Sure, that was mistake in my patch from previous reply.
>>
>>>
>>> gcc/cgraph.c
>>> /* Return true if this edge has multiple indirect call targets.  */
>>>   bool
>>>   cgraph_edge::has_multiple_indirect_call_p (void)
>>>   {
>>> -  return indirect_info && indirect_info->indirect_call_targets
>>> -        && indirect_info->indirect_call_targets->length () > 1;
>>> +  return (indirect_info && indirect_info->indirect_call_targets
>>> +         && indirect_info->indirect_call_targets->length () > 1);
>>>   }
>>>
>>>   /* Return true if this edge has at least one indirect call target.  */
>>>   bool
>>>   cgraph_edge::has_indirect_call_p (void)
>>>   {
>>> -  return indirect_info && indirect_info->indirect_call_targets
>>> -        && indirect_info->indirect_call_targets->length ();
>>> +  return (indirect_info && indirect_info->indirect_call_targets
>>> +         && !indirect_info->indirect_call_targets->is_empty ());
>>>   }
>>>
>>>>
>>>> I see following failures for the tests provided:
>>>> FAIL: gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c compilation,  
>>>> -fprofile-generate -D_PROFILE_GENERATE
>>>> FAIL: gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c compilation,  
>>>> -fprofile-generate -D_PROFILE_GENERATE
>>>> FAIL: gcc.dg/tree-prof/indir-call-prof-topn.c compilation,  
>>>> -fprofile-generate -D_PROFILE_GENERATE
>>>
>>> Sorry that I forgot to remove the deprecated build option in the 3 cases
>>> (also updated the scan exp check):
>>> -/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate 
>>> --param indir-call-topn-profile=1" } */
>>> +/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate" } */
>>>
>>>
>>> The new patch is attached.  Thanks.
>>
>> Hm, looking at the gimple_ic_transform function. I think the function 
>> should always
>> return false as it never does a GIMPLE transformation.
>>
>> Apart from that, I'm fine with the patch. Note that I'm not the 
>> maintainer, but I bet we simplified
>> the patch review to Honza significantly.
>>
>> Last missing piece is probably the update ChangeLog.
>>
>> Thank you for working on that,
>> Martin
>>
>>>
>>>
>>> Xiong Hu
>>>
>>>>
>>>> Next comments follow directly in the email body:
>>>>
>>>>>
>>>>> v4 Changes:
>>>>>    1. Rebase to trunk.
>>>>>    2. Remove num_of_ics and use vector's length to avoid redundancy.
>>>>>    3. Update the code in ipa-profile.c to improve review feasibility.
>>>>>    4. Add function has_indirect_call_p and has_multiple_indirect_call_p.
>>>>>    5. For parameter control, I will leave it to next patch as it is a
>>>>>       relative independent function.  Currently, maximum number of
>>>>>       promotions is GCOV_TOPN_VALUES as only 4 profiling value limited
>>>>>       from profile-generate, therefore minimum probability is adjusted to
>>>>>       25% in value-prof.c, it was 75% also by hard code for single
>>>>>       indirect target.  No control to minimal number of edge
>>>>>       executions yet.  What's more, this patch is a bit large now.
>>>>>
>>>>> This patch aims to fix PR69678 caused by PGO indirect call profiling
>>>>> performance issues.
>>>>> The bug that profiling data is never working was fixed by Martin's pull
>>>>> back of topN patches, performance got GEOMEAN ~1% improvement(+24% for
>>>>> 511.povray_r specifically).
>>>>> Still, currently the default profile only generates SINGLE indirect 
>>>>> target
>>>>> that called more than 75%.  This patch leverages MULTIPLE indirect
>>>>> targets use in LTO-WPA and LTO-LTRANS stage, as a result, function
>>>>> specialization, profiling, partial devirtualization, inlining and
>>>>> cloning could be done successfully based on it.
>>>>> Performance can get improved from 0.70 sec to 0.38 sec on simple tests.
>>>>> Details are:
>>>>>     1.  PGO with topn is enabled by default now, but only one indirect
>>>>>     target edge will be generated in ipa-profile pass, so add 
>>>>> variables to enable
>>>>>     multiple speculative edges through passes, speculative_id will 
>>>>> record the
>>>>>     direct edge index bind to the indirect edge, indirect_call_targets 
>>>>> length
>>>>>     records how many direct edges owned by the indirect edge, postpone 
>>>>> gimple_ic
>>>>>     to ipa-profile like default as inline pass will decide whether it 
>>>>> is benefit
>>>>>     to transform indirect call.
>>>>>     2.  Use speculative_id to track and search the reference node matched
>>>>>     with the direct edge's callee for multiple targets.  Actually, it 
>>>>> is the
>>>>>     caller's responsibility to handle the direct edges mapped to same 
>>>>> indirect
>>>>>     edge.  speculative_call_info will return one of the direct edge 
>>>>> specified,
>>>>>     this will leverage current IPA edge process framework mostly.
>>>>>     3.  Enable LTO WPA/LTRANS stage multiple indirect call targets 
>>>>> analysis for
>>>>>     profile full support in ipa passes and cgraph_edge functions.  
>>>>> speculative_id
>>>>>     can be set by make_speculative id when multiple targets are binded to
>>>>>     one indirect edge, and cloned if new edge is cloned.  speculative_id
>>>>>     is streamed out and stream int by lto like lto_stmt_uid.
>>>>>     4.  Add 1 in module testcase and 2 cross module testcases.
>>>>>     5.  Bootstrap and regression test passed on Power8-LE.  No function
>>>>>     and performance regression for SPEC2017.
>>>>>
>>>>> gcc/ChangeLog
>>>>>
>>>>>      2019-09-25  Xiong Hu Luo  <luoxhu@linux.ibm.com>
>>>>>
>>>>>      PR ipa/69678
>>>>>      * cgraph.c (symbol_table::create_edge): Init speculative_id.
>>>>>      (cgraph_edge::make_speculative): Add param for setting 
>>>>> speculative_id.
>>>>>      (cgraph_edge::speculative_call_info): Find reference by
>>>>>      speculative_id for multiple indirect targets.
>>>>>      (cgraph_edge::resolve_speculation): Decrease the speculations
>>>>>      for indirect edge, drop it's speculative if not direct target
>>>>>      left.
>>>>>      (cgraph_edge::redirect_call_stmt_to_callee): Likewise.
>>>>>      (cgraph_node::verify_node): Don't report error if speculative
>>>>>      edge not include statement.
>>>>>      (cgraph_edge::has_multiple_indirect_call_p): New function.
>>>>>      (cgraph_edge::has_indirect_call_p): New function.
>>>>>      * cgraph.h (struct indirect_target_info): New struct.
>>>>>      (indirect_call_targets): New vector variable.
>>>>>      (make_speculative): Add param for setting speculative_id.
>>>>>      (cgraph_edge::has_multiple_indirect_call_p): New declare.
>>>>>      (cgraph_edge::has_indirect_call_p): New declare.
>>>>>      (speculative_id): New variable.
>>>>>      * cgraphclones.c (cgraph_node::create_clone): Clone speculative_id.
>>>>>      * ipa-inline.c (inline_small_functions): Fix iterator update.
>>>>>      * ipa-profile.c (ipa_profile_generate_summary): Add indirect
>>>>>      multiple targets logic.
>>>>>      (ipa_profile): Likewise.
>>>>>      * ipa-ref.h (speculative_id): New variable.
>>>>>      * ipa.c (process_references): Fix typo.
>>>>>      * lto-cgraph.c (lto_output_edge): Add indirect multiple targets
>>>>>      logic.  Stream out speculative_id.
>>>>>      (input_edge): Likewise.
>>>>>      * predict.c (dump_prediction): Remove edges count assert to be
>>>>>      precise.
>>>>>      * symtab.c (symtab_node::create_reference): Init speculative_id.
>>>>>      (symtab_node::clone_references): Clone speculative_id.
>>>>>      (symtab_node::clone_referring): Clone speculative_id.
>>>>>      (symtab_node::clone_reference): Clone speculative_id.
>>>>>      (symtab_node::clear_stmts_in_references): Clear speculative_id.
>>>>>      * tree-inline.c (copy_bb): Duplicate all the speculative edges
>>>>>      if indirect call contains multiple speculative targets.
>>>>>      * tree-profile.c (gimple_gen_ic_profiler): Use the new variable
>>>>>      __gcov_indirect_call.counters and __gcov_indirect_call.callee.
>>>>>      (gimple_gen_ic_func_profiler): Likewise.
>>>>>      (pass_ipa_tree_profile::gate): Fix comment typos.
>>>>>      * value-prof.c  (gimple_ic_transform): Handle topn case.
>>>>>      Fix comment typos.
>>>>>
>>>>> gcc/testsuite/ChangeLog
>>>>>
>>>>>      2019-09-25  Xiong Hu Luo  <luoxhu@linux.ibm.com>
>>>>>
>>>>>      PR ipa/69678
>>>>>      * gcc.dg/tree-prof/indir-call-prof-topn.c: New testcase.
>>>>>      * gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c: New testcase.
>>>>>      * gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c: New testcase.
>>>>>      * gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c: New testcase.
>>>>> ---
>>>>>    gcc/cgraph.c                                  | 90 ++++++++++++++++-
>>>>>    gcc/cgraph.h                                  | 29 +++++-
>>>>>    gcc/cgraphclones.c                            |  1 +
>>>>>    gcc/ipa-inline.c                              | 15 +--
>>>>>    gcc/ipa-profile.c                             | 96 ++++++++++++++-----
>>>>>    gcc/ipa-ref.h                                 |  1 +
>>>>>    gcc/ipa.c                                     |  2 +-
>>>>>    gcc/lto-cgraph.c                              | 57 +++++++++--
>>>>>    gcc/predict.c                                 |  1 -
>>>>>    gcc/symtab.c                                  |  5 +
>>>>>    .../tree-prof/crossmodule-indir-call-topn-1.c | 35 +++++++
>>>>>    .../crossmodule-indir-call-topn-1a.c          | 22 +++++
>>>>>    .../tree-prof/crossmodule-indir-call-topn-2.c | 42 ++++++++
>>>>>    .../gcc.dg/tree-prof/indir-call-prof-topn.c   | 38 ++++++++
>>>>>    gcc/tree-inline.c                             | 19 ++++
>>>>>    gcc/tree-profile.c                            | 12 +--
>>>>>    gcc/value-prof.c                              | 86 +++++++++--------
>>>>>    17 files changed, 452 insertions(+), 99 deletions(-)
>>>>>    create mode 100644 
>>>>> gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
>>>>>    create mode 100644 
>>>>> gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
>>>>>    create mode 100644 
>>>>> gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
>>>>>    create mode 100644 
>>>>> gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
>>>>>
>>>>> diff --git a/gcc/cgraph.c b/gcc/cgraph.c
>>>>> index 843891e9e56..9a28aca435f 100644
>>>>> --- a/gcc/cgraph.c
>>>>> +++ b/gcc/cgraph.c
>>>>> @@ -860,6 +860,7 @@ symbol_table::create_edge (cgraph_node *caller, 
>>>>> cgraph_node *callee,
>>>>>      edge->prev_callee = NULL;
>>>>>      edge->next_callee = NULL;
>>>>>      edge->lto_stmt_uid = 0;
>>>>> +  edge->speculative_id = 0;
>>>>>        edge->count = count;
>>>>>    @@ -1051,7 +1052,8 @@ cgraph_edge::remove (void)
>>>>>       Return direct edge created.  */
>>>>>      cgraph_edge *
>>>>> -cgraph_edge::make_speculative (cgraph_node *n2, profile_count 
>>>>> direct_count)
>>>>> +cgraph_edge::make_speculative (cgraph_node *n2, profile_count 
>>>>> direct_count,
>>>>> +                   unsigned int speculative_id)
>>>>>    {
>>>>>      cgraph_node *n = caller;
>>>>>      ipa_ref *ref = NULL;
>>>>> @@ -1069,11 +1071,13 @@ cgraph_edge::make_speculative (cgraph_node 
>>>>> *n2, profile_count direct_count)
>>>>>      else
>>>>>        e2->can_throw_external = can_throw_external;
>>>>>      e2->lto_stmt_uid = lto_stmt_uid;
>>>>> +  e2->speculative_id = speculative_id;
>>>>>      e2->in_polymorphic_cdtor = in_polymorphic_cdtor;
>>>>>      count -= e2->count;
>>>>>      symtab->call_edge_duplication_hooks (this, e2);
>>>>>      ref = n->create_reference (n2, IPA_REF_ADDR, call_stmt);
>>>>>      ref->lto_stmt_uid = lto_stmt_uid;
>>>>> +  ref->speculative_id = speculative_id;
>>>>>      ref->speculative = speculative;
>>>>>      n2->mark_address_taken ();
>>>>>      return e2;
>>>>> @@ -1087,6 +1091,38 @@ cgraph_edge::make_speculative (cgraph_node *n2, 
>>>>> profile_count direct_count)
>>>>>       call) and if one of them exists, all of them must exist.
>>>>>         Given speculative call edge, return all three components.
>>>>> +
>>>>> +   For some indirect edge, it may maps to multiple direct edges, 
>>>>> i.e.  1:N.
>>>>> +   check the speculative_id to return all the three components for 
>>>>> specified
>>>>> +   direct edge or indirect edge.
>>>>> +   If input is indirect, caller of this function will get the direct 
>>>>> edge one by
>>>>> +   one, get_edge will just return one of the direct edge mapped to 
>>>>> the indirect
>>>>> +   edge, the returned direct edge will be resolved or redirected by 
>>>>> the caller,
>>>>> +   then number of indirect calls (speculations) is deceased in each 
>>>>> access.
>>>>> +   If input is direct, this function will get the indirect edge and 
>>>>> reference
>>>>> +   with matched speculative_id, the returned edge will also be 
>>>>> resolved or
>>>>> +   redirected, decrease the speculations accordingly.
>>>>> +   Speculations of indirect edge will be dropped only if all direct 
>>>>> edges
>>>>> +   be handled.
>>>>> +
>>>>> +   e.g.  for indirect edge E statement "call call_dest":
>>>>> +
>>>>> +   Redirect N3 after redirected N2:
>>>>> +
>>>>> +   if (call_dest == N2)
>>>>> +     n2 ();
>>>>> +   else if (call_dest == N3)
>>>>> +     n3 ();
>>>>> +   else
>>>>> +     call call_dest
>>>>> +
>>>>> +   Resolve N3 and only redirect N2:
>>>>> +
>>>>> +   if (call_dest == N2)
>>>>> +     n2 ();
>>>>> +   else
>>>>> +     call call_dest
>>>>> +
>>>>>     */
>>>>>      void
>>>>> @@ -1126,7 +1162,7 @@ cgraph_edge::speculative_call_info (cgraph_edge 
>>>>> *&direct,
>>>>>        reference = NULL;
>>>>>      for (i = 0; e->caller->iterate_reference (i, ref); i++)
>>>>> -    if (ref->speculative
>>>>> +    if (ref->speculative && ref->speculative_id == e->speculative_id
>>>>>        && ((ref->stmt && ref->stmt == e->call_stmt)
>>>>>            || (!ref->stmt && ref->lto_stmt_uid == e->lto_stmt_uid)))
>>>>>          {
>>>>> @@ -1187,7 +1223,21 @@ cgraph_edge::resolve_speculation (tree 
>>>>> callee_decl)
>>>>>             in the functions inlined through it.  */
>>>>>        }
>>>>>      edge->count += e2->count;
>>>>> -  edge->speculative = false;
>>>>> +  /* edge is indirect, e2 is direct.  If edge contains multiple 
>>>>> speculations,
>>>>> +     remove one of speculations for this indirect edge, then if edge 
>>>>> still
>>>>> +     contains direct target, keep the speculation, next direct target
>>>>> +     will continue use it.  Give up speculation completely if no direct
>>>>> +     target is left for this indirect edge.  */
>>>>> +  if (edge->has_indirect_call_p ())
>>>>> +    {
>>>>> +      /* As the direct targets are sorted by decrease, delete the 
>>>>> first target
>>>>> +     when it is resolved.  */
>>>>> +      edge->indirect_info->indirect_call_targets->ordered_remove (0);
>>>>> +      if (!edge->indirect_info->indirect_call_targets->length ())
>>>>> +    edge->speculative = false;
>>>>> +    }
>>>>> +  else
>>>>> +    edge->speculative = false;
>>>>>      e2->speculative = false;
>>>>>      ref->remove_reference ();
>>>>>      if (e2->indirect_unknown_callee || e2->inline_failed)
>>>>> @@ -1321,7 +1371,21 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
>>>>>          e->caller->set_call_stmt_including_clones (e->call_stmt, 
>>>>> new_stmt,
>>>>>                                 false);
>>>>>          e->count = gimple_bb (e->call_stmt)->count;
>>>>> -      e2->speculative = false;
>>>>> +      /* edge is direct, e2 is indirect here.  If e2 contains multiple
>>>>> +         speculations, remove one of speculations for this indirect 
>>>>> edge,
>>>>> +         then if e2 still contains direct target, keep the speculation,
>>>>> +         next direct target will continue use it.  Give up speculation
>>>>> +         completely if no direct target is left for this indirect 
>>>>> e2.  */
>>>>> +      if (e2->has_indirect_call_p ())
>>>>> +        {
>>>>> +          /* As the direct targets are sorted by decrease, delete the 
>>>>> first
>>>>> +         target when it is redirected.  */
>>>>> +          e2->indirect_info->indirect_call_targets->ordered_remove (0);
>>>>> +          if (!e2->indirect_info->indirect_call_targets->length ())
>>>>> +        e2->speculative = false;
>>>>> +        }
>>>>> +      else
>>>>> +        e2->speculative = false;
>>>>>          e2->count = gimple_bb (e2->call_stmt)->count;
>>>>>          ref->speculative = false;
>>>>>          ref->stmt = NULL;
>>>>> @@ -3445,7 +3509,7 @@ cgraph_node::verify_node (void)
>>>>>            for (e = callees; e; e = e->next_callee)
>>>>>        {
>>>>> -      if (!e->aux)
>>>>> +      if (!e->aux && !e->speculative)
>>>>>            {
>>>>>              error ("edge %s->%s has no corresponding call_stmt",
>>>>>                 identifier_to_locale (e->caller->name ()),
>>>>> @@ -3872,6 +3936,22 @@ 
>>>>> cgraph_edge::possibly_call_in_translation_unit_p (void)
>>>>>      return node->get_availability () >= AVAIL_AVAILABLE;
>>>>>    }
>>>>>    +/* Return true if this edge has multiple indirect call targets.  */
>>>>> +bool
>>>>> +cgraph_edge::has_multiple_indirect_call_p (void)
>>>>> +{
>>>>> +  return indirect_info && indirect_info->indirect_call_targets
>>>>> +     && indirect_info->indirect_call_targets->length () > 1;
>>>>> +}
>>>>
>>>> For multiline && expression, we typically wrap the whole condition
>>>> in '(' and ')'.
>>>>
>>>>> +
>>>>> +/* Return true if this edge has at least one indirect call target.  */
>>>>> +bool
>>>>> +cgraph_edge::has_indirect_call_p (void)
>>>>> +{
>>>>> +  return indirect_info && indirect_info->indirect_call_targets
>>>>> +     && indirect_info->indirect_call_targets->length ();
>>>>> +}
>>>>
>>>> Likewise here.
>>>>
>>>>> +
>>>>>    /* A stashed copy of "symtab" for use by selftest::symbol_table_test.
>>>>>       This needs to be a global so that it can be a GC root, and thus
>>>>>       prevent the stashed copy from being garbage-collected if the GC 
>>>>> runs
>>>>> diff --git a/gcc/cgraph.h b/gcc/cgraph.h
>>>>> index 4c54210123a..33c8454c4e0 100644
>>>>> --- a/gcc/cgraph.h
>>>>> +++ b/gcc/cgraph.h
>>>>> @@ -1636,6 +1636,16 @@ private:
>>>>>      void make_speculative (tree otr_type = NULL);
>>>>>    };
>>>>>    +/* Structure containing indirect target information from profile.  */
>>>>> +
>>>>> +struct GTY (()) indirect_target_info
>>>>> +{
>>>>> +  /* Profile_id of common target obtained from profile.  */
>>>>> +  unsigned int common_target_id;
>>>>> +  /* Probability that call will land in function with 
>>>>> COMMON_TARGET_ID.  */
>>>>> +  int common_target_probability;
>>>>> +};
>>>>> +
>>>>>    /* Structure containing additional information about an indirect 
>>>>> call.  */
>>>>>      class GTY(()) cgraph_indirect_call_info
>>>>> @@ -1654,10 +1664,9 @@ public:
>>>>>      int param_index;
>>>>>      /* ECF flags determined from the caller.  */
>>>>>      int ecf_flags;
>>>>> -  /* Profile_id of common target obtrained from profile.  */
>>>>> -  int common_target_id;
>>>>> -  /* Probability that call will land in function with 
>>>>> COMMON_TARGET_ID.  */
>>>>> -  int common_target_probability;
>>>>> +
>>>>> +  /* An indirect call may contain one or multiple call targets.  */
>>>>> +  vec<indirect_target_info, va_gc> *indirect_call_targets;
>>>>>        /* Set when the call is a virtual call with the parameter being 
>>>>> the
>>>>>         associated object pointer rather than a simple direct call.  */
>>>>> @@ -1714,7 +1723,8 @@ public:
>>>>>      /* Turn edge into speculative call calling N2. Update
>>>>>         the profile so the direct call is taken COUNT times
>>>>>         with FREQUENCY.  */
>>>>> -  cgraph_edge *make_speculative (cgraph_node *n2, profile_count 
>>>>> direct_count);
>>>>> +  cgraph_edge *make_speculative (cgraph_node *n2, profile_count 
>>>>> direct_count,
>>>>> +                 unsigned int speculative_id = 0);
>>>>>         /* Given speculative call edge, return all three components.  */
>>>>>      void speculative_call_info (cgraph_edge *&direct, cgraph_edge 
>>>>> *&indirect,
>>>>> @@ -1773,6 +1783,12 @@ public:
>>>>>         be internal to the current translation unit.  */
>>>>>      bool possibly_call_in_translation_unit_p (void);
>>>>>    +  /* Return true if this edge has multiple indirect call targets.  */
>>>>> +  bool has_multiple_indirect_call_p (void);
>>>>> +
>>>>> +  /* Return true if this edge has at least one indirect call target.  */
>>>>> +  bool has_indirect_call_p (void);
>>>>> +
>>>>>      /* Expected number of executions: calculated in profile.c.  */
>>>>>      profile_count count;
>>>>>      cgraph_node *caller;
>>>>> @@ -1792,6 +1808,9 @@ public:
>>>>>      /* The stmt_uid of call_stmt.  This is used by LTO to recover the 
>>>>> call_stmt
>>>>>         when the function is serialized in.  */
>>>>>      unsigned int lto_stmt_uid;
>>>>> +  /* speculative id is used by multiple indirect targets when the 
>>>>> function is
>>>>> +   speculated.  */
>>>>> +  unsigned int speculative_id;
>>>>>      /* Whether this edge was made direct by indirect inlining.  */
>>>>>      unsigned int indirect_inlining_edge : 1;
>>>>>      /* Whether this edge describes an indirect call with an undetermined
>>>>> diff --git a/gcc/cgraphclones.c b/gcc/cgraphclones.c
>>>>> index fa753697c78..5dbd8d90b77 100644
>>>>> --- a/gcc/cgraphclones.c
>>>>> +++ b/gcc/cgraphclones.c
>>>>> @@ -128,6 +128,7 @@ cgraph_edge::clone (cgraph_node *n, gcall 
>>>>> *call_stmt, unsigned stmt_uid,
>>>>>      new_edge->inline_failed = inline_failed;
>>>>>      new_edge->indirect_inlining_edge = indirect_inlining_edge;
>>>>>      new_edge->lto_stmt_uid = stmt_uid;
>>>>> +  new_edge->speculative_id = speculative_id;
>>>>>      /* Clone flags that depend on call_stmt availability manually.  */
>>>>>      new_edge->can_throw_external = can_throw_external;
>>>>>      new_edge->call_stmt_cannot_inline_p = call_stmt_cannot_inline_p;
>>>>> diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
>>>>> index b62d280eb25..6136214f9ac 100644
>>>>> --- a/gcc/ipa-inline.c
>>>>> +++ b/gcc/ipa-inline.c
>>>>> @@ -1881,12 +1881,15 @@ inline_small_functions (void)
>>>>>        }
>>>>>          if (has_speculative)
>>>>>        for (edge = node->callees; edge; edge = next)
>>>>> -      if (edge->speculative && !speculation_useful_p (edge,
>>>>> -                              edge->aux != NULL))
>>>>> -        {
>>>>> -          edge->resolve_speculation ();
>>>>> -          update = true;
>>>>> -        }
>>>>> +      {
>>>>> +        next = edge->next_callee;
>>>>> +        if (edge->speculative
>>>>> +        && !speculation_useful_p (edge, edge->aux != NULL))
>>>>> +          {
>>>>> +        edge->resolve_speculation ();
>>>>> +        update = true;
>>>>> +          }
>>>>> +      }
>>>>>          if (update)
>>>>>        {
>>>>>          struct cgraph_node *where = node->global.inlined_to
>>>>> diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
>>>>> index 970dba39c80..342e8ea05d1 100644
>>>>> --- a/gcc/ipa-profile.c
>>>>> +++ b/gcc/ipa-profile.c
>>>>> @@ -192,23 +192,35 @@ ipa_profile_generate_summary (void)
>>>>>              if (h)
>>>>>                {
>>>>>                  gcov_type val, count, all;
>>>>> -              if (get_nth_most_common_value (NULL, "indirect call", h,
>>>>> -                             &val, &count, &all))
>>>>> +              struct cgraph_edge *e = node->get_edge (stmt);
>>>>> +              if (e && !e->indirect_unknown_callee)
>>>>> +            continue;
>>>>> +
>>>>> +              struct indirect_target_info item;
>>>>> +              for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
>>>>>                {
>>>>> -              struct cgraph_edge * e = node->get_edge (stmt);
>>>>> -              if (e && !e->indirect_unknown_callee)
>>>>> +              if (!get_nth_most_common_value (NULL, "indirect call",
>>>>> +                              h, &val, &count, &all,
>>>>> +                              j))
>>>>> +                continue;
>>>>> +
>>>>> +              if (val == 0)
>>>>>                    continue;
>>>>>    -              e->indirect_info->common_target_id = val;
>>>>> -              e->indirect_info->common_target_probability
>>>>> +              item.common_target_id = val;
>>>>> +              item.common_target_probability
>>>>>                    = GCOV_COMPUTE_SCALE (count, all);
>>>>
>>>> There's one of the places where you can use the constructor.
>>>>
>>>>> -              if (e->indirect_info->common_target_probability > 
>>>>> REG_BR_PROB_BASE)
>>>>> +              if (item.common_target_probability > REG_BR_PROB_BASE)
>>>>>                    {
>>>>>                      if (dump_file)
>>>>> -                fprintf (dump_file, "Probability capped to 1\n");
>>>>> -                  e->indirect_info->common_target_probability = 
>>>>> REG_BR_PROB_BASE;
>>>>> +                fprintf (dump_file,
>>>>> +                     "Probability capped to 1\n");
>>>>> +                  item.common_target_probability = REG_BR_PROB_BASE;
>>>>>                    }
>>>>> +              vec_safe_push (
>>>>> +                e->indirect_info->indirect_call_targets, item);
>>>>>                }
>>>>> +
>>>>>                  gimple_remove_histogram_value (DECL_STRUCT_FUNCTION 
>>>>> (node->decl),
>>>>>                                  stmt, h);
>>>>>                }
>>>>> @@ -492,6 +504,7 @@ ipa_profile (void)
>>>>>      int nindirect = 0, ncommon = 0, nunknown = 0, nuseless = 0, 
>>>>> nconverted = 0;
>>>>>      int nmismatch = 0, nimpossible = 0;
>>>>>      bool node_map_initialized = false;
>>>>> +  gcov_type threshold;
>>>>>        if (dump_file)
>>>>>        dump_histogram (dump_file, histogram);
>>>>> @@ -500,14 +513,12 @@ ipa_profile (void)
>>>>>          overall_time += histogram[i]->count * histogram[i]->time;
>>>>>          overall_size += histogram[i]->size;
>>>>>        }
>>>>> +  threshold = 0;
>>>>>      if (overall_time)
>>>>>        {
>>>>> -      gcov_type threshold;
>>>>> -
>>>>>          gcc_assert (overall_size);
>>>>>            cutoff = (overall_time * PARAM_VALUE 
>>>>> (HOT_BB_COUNT_WS_PERMILLE) + 500) / 1000;
>>>>> -      threshold = 0;
>>>>>          for (i = 0; cumulated < cutoff; i++)
>>>>>        {
>>>>>          cumulated += histogram[i]->count * histogram[i]->time;
>>>>> @@ -543,7 +554,7 @@ ipa_profile (void)
>>>>>      histogram.release ();
>>>>>      histogram_pool.release ();
>>>>>    -  /* Produce speculative calls: we saved common traget from 
>>>>> porfiling into
>>>>> +  /* Produce speculative calls: we saved common target from profiling 
>>>>> into
>>>>>         e->common_target_id.  Now, at link time, we can look up 
>>>>> corresponding
>>>>>         function node and produce speculative call.  */
>>>>>    @@ -558,13 +569,37 @@ ipa_profile (void)
>>>>>        {
>>>>>          if (n->count.initialized_p ())
>>>>>            nindirect++;
>>>>> -      if (e->indirect_info->common_target_id)
>>>>> +      if (e->has_indirect_call_p ())
>>>>>            {
>>>>>              if (!node_map_initialized)
>>>>> -            init_node_map (false);
>>>>> +        init_node_map (false);
>>>>>              node_map_initialized = true;
>>>>>              ncommon++;
>>>>> -          n2 = find_func_by_profile_id 
>>>>> (e->indirect_info->common_target_id);
>>>>> +
>>>>> +          if (in_lto_p)
>>>>> +        {
>>>>> +          if (dump_file)
>>>>> +            {
>>>>> +              fprintf (dump_file,
>>>>> +                   "Updating hotness threshold in LTO mode.\n");
>>>>> +              fprintf (dump_file, "Updated min count: %" PRId64 "\n",
>>>>> +                   (int64_t) threshold);
>>>>> +            }
>>>>> +          set_hot_bb_threshold (threshold
>>>>> +            / e->indirect_info->indirect_call_targets->length ());
>>>>> +        }
>>>>> +
>>>>> +          unsigned speculative_id = 0;
>>>>> +          struct indirect_target_info *item;
>>>>> +          /* The code below is not formatted yet for review convenience.
>>>>> +         Move to a seprate small function is not easy as too many local
>>>>> +         variables used in it.  Need format and remove this comments
>>>>> +         once got approved.  */
>>>>> +          FOR_EACH_VEC_SAFE_ELT 
>>>>> (e->indirect_info->indirect_call_targets, i,
>>>>> +                     item)
>>>>> +           {
>>>>> +          bool speculative_found = false;
>>>>> +          n2 = find_func_by_profile_id (item->common_target_id);
>>>>>              if (n2)
>>>>>            {
>>>>>              if (dump_file)
>>>>> @@ -573,11 +608,10 @@ ipa_profile (void)
>>>>>                       " other module %s => %s, prob %3.2f\n",
>>>>>                       n->dump_name (),
>>>>>                       n2->dump_name (),
>>>>> -                   e->indirect_info->common_target_probability
>>>>> -                   / (float)REG_BR_PROB_BASE);
>>>>> +                   item->common_target_probability
>>>>> +                 / (float) REG_BR_PROB_BASE);
>>>>>                }
>>>>> -          if (e->indirect_info->common_target_probability
>>>>> -              < REG_BR_PROB_BASE / 2)
>>>>> +          if (item->common_target_probability < REG_BR_PROB_BASE / 2)
>>>>>                {
>>>>>                  nuseless++;
>>>>>                  if (dump_file)
>>>>> @@ -613,7 +647,7 @@ ipa_profile (void)
>>>>>                  if (dump_file)
>>>>>                fprintf (dump_file,
>>>>>                     "Not speculating: "
>>>>> -                 "parameter count mistmatch\n");
>>>>> +                 "parameter count mismatch\n");
>>>>>                }
>>>>>              else if (e->indirect_info->polymorphic
>>>>>                   && !opt_for_fn (n->decl, flag_devirtualize)
>>>>> @@ -640,20 +674,30 @@ ipa_profile (void)
>>>>>                    n2 = alias;
>>>>>                }
>>>>>                  nconverted++;
>>>>> -              e->make_speculative
>>>>> -            (n2,
>>>>> -             e->count.apply_probability
>>>>> -                     (e->indirect_info->common_target_probability));
>>>>> +              e->make_speculative (n2,
>>>>> +                       e->count.apply_probability (
>>>>> +                         item->common_target_probability),
>>>>> +                       speculative_id);
>>>>>                  update = true;
>>>>> +              speculative_id++;
>>>>> +              speculative_found = true;
>>>>>                }
>>>>>            }
>>>>>              else
>>>>>            {
>>>>>              if (dump_file)
>>>>>                fprintf (dump_file, "Function with profile-id %i not 
>>>>> found.\n",
>>>>> -                 e->indirect_info->common_target_id);
>>>>> +                 item->common_target_id);
>>>>>              nunknown++;
>>>>>            }
>>>>> +          if (!speculative_found)
>>>>> +        {
>>>>> +          /* Remove item from indirect_call_targets if no
>>>>> +             speculative edge generated, rollback the iteration.  */
>>>>> +          e->indirect_info->indirect_call_targets->ordered_remove (i);
>>>>> +          i--;
>>>>> +        }
>>>>> +           }
>>>>>            }
>>>>>         }
>>>>>           if (update)
>>>>> diff --git a/gcc/ipa-ref.h b/gcc/ipa-ref.h
>>>>> index 0d8e509c932..3e6562ec9d1 100644
>>>>> --- a/gcc/ipa-ref.h
>>>>> +++ b/gcc/ipa-ref.h
>>>>> @@ -59,6 +59,7 @@ public:
>>>>>      symtab_node *referred;
>>>>>      gimple *stmt;
>>>>>      unsigned int lto_stmt_uid;
>>>>> +  unsigned int speculative_id;
>>>>>      unsigned int referred_index;
>>>>>      ENUM_BITFIELD (ipa_ref_use) use:3;
>>>>>      unsigned int speculative:1;
>>>>> diff --git a/gcc/ipa.c b/gcc/ipa.c
>>>>> index 6b84e1f9bda..a10b0603f14 100644
>>>>> --- a/gcc/ipa.c
>>>>> +++ b/gcc/ipa.c
>>>>> @@ -166,7 +166,7 @@ process_references (symtab_node *snode,
>>>>>       devirtualization happens.  After inlining still keep their 
>>>>> declarations
>>>>>       around, so we can devirtualize to a direct call.
>>>>>    -   Also try to make trivial devirutalization when no or only one 
>>>>> target is
>>>>> +   Also try to make trivial devirtualization when no or only one 
>>>>> target is
>>>>>       possible.  */
>>>>>      static void
>>>>> diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
>>>>> index bc0f0107333..61380dcc7b8 100644
>>>>> --- a/gcc/lto-cgraph.c
>>>>> +++ b/gcc/lto-cgraph.c
>>>>> @@ -238,6 +238,7 @@ lto_output_edge (struct lto_simple_output_block 
>>>>> *ob, struct cgraph_edge *edge,
>>>>>      unsigned int uid;
>>>>>      intptr_t ref;
>>>>>      struct bitpack_d bp;
>>>>> +  unsigned len;
>>>>>        if (edge->indirect_unknown_callee)
>>>>>        streamer_write_enum (ob->main_stream, LTO_symtab_tags, 
>>>>> LTO_symtab_last_tag,
>>>>> @@ -265,6 +266,7 @@ lto_output_edge (struct lto_simple_output_block 
>>>>> *ob, struct cgraph_edge *edge,
>>>>>      bp_pack_enum (&bp, cgraph_inline_failed_t,
>>>>>                CIF_N_REASONS, edge->inline_failed);
>>>>>      bp_pack_var_len_unsigned (&bp, uid);
>>>>> +  bp_pack_var_len_unsigned (&bp, edge->speculative_id);
>>>>>      bp_pack_value (&bp, edge->indirect_inlining_edge, 1);
>>>>>      bp_pack_value (&bp, edge->speculative, 1);
>>>>>      bp_pack_value (&bp, edge->call_stmt_cannot_inline_p, 1);
>>>>> @@ -291,11 +293,27 @@ lto_output_edge (struct lto_simple_output_block 
>>>>> *ob, struct cgraph_edge *edge,
>>>>>      streamer_write_bitpack (&bp);
>>>>>      if (edge->indirect_unknown_callee)
>>>>>        {
>>>>> -      streamer_write_hwi_stream (ob->main_stream,
>>>>> -                     edge->indirect_info->common_target_id);
>>>>> -      if (edge->indirect_info->common_target_id)
>>>>> -    streamer_write_hwi_stream
>>>>> -       (ob->main_stream, 
>>>>> edge->indirect_info->common_target_probability);
>>>>> +      struct indirect_target_info *item;
>>>>> +      unsigned int i;
>>>>> +      len = edge->has_indirect_call_p ()
>>>>> +          ? edge->indirect_info->indirect_call_targets->length ()
>>>>> +          : 0;
>>>>> +      gcc_assert (len <= GCOV_TOPN_VALUES);
>>>>> +
>>>>> +      streamer_write_hwi_stream (ob->main_stream, len);
>>>>> +
>>>>> +      if (len)
>>>>> +    {
>>>>> +      FOR_EACH_VEC_SAFE_ELT 
>>>>> (edge->indirect_info->indirect_call_targets, i,
>>>>> +                 item)
>>>>> +        {
>>>>> +          streamer_write_hwi_stream (ob->main_stream,
>>>>> +                     item->common_target_id);
>>>>> +          if (item->common_target_id)
>>>>> +        streamer_write_hwi_stream (ob->main_stream,
>>>>> +                       item->common_target_probability);
>>>>> +        }
>>>>> +    }
>>>>>        }
>>>>>    }
>>>>>    @@ -688,6 +706,7 @@ lto_output_ref (struct lto_simple_output_block 
>>>>> *ob, struct ipa_ref *ref,
>>>>>          if (ref->stmt)
>>>>>        uid = gimple_uid (ref->stmt) + 1;
>>>>>          streamer_write_hwi_stream (ob->main_stream, uid);
>>>>> +      streamer_write_hwi_stream (ob->main_stream, ref->speculative_id);
>>>>>        }
>>>>>    }
>>>>>    @@ -1419,7 +1438,10 @@ input_ref (class lto_input_block *ib,
>>>>>      ref = referring_node->create_reference (node, use);
>>>>>      ref->speculative = speculative;
>>>>>      if (is_a <cgraph_node *> (referring_node))
>>>>> -    ref->lto_stmt_uid = streamer_read_hwi (ib);
>>>>> +    {
>>>>> +      ref->lto_stmt_uid = streamer_read_hwi (ib);
>>>>> +      ref->speculative_id = streamer_read_hwi (ib);
>>>>> +    }
>>>>>    }
>>>>>      /* Read an edge from IB.  NODES points to a vector of previously 
>>>>> read nodes for
>>>>> @@ -1433,11 +1455,12 @@ input_edge (class lto_input_block *ib, 
>>>>> vec<symtab_node *> nodes,
>>>>>    {
>>>>>      struct cgraph_node *caller, *callee;
>>>>>      struct cgraph_edge *edge;
>>>>> -  unsigned int stmt_id;
>>>>> +  unsigned int stmt_id, speculative_id;
>>>>>      profile_count count;
>>>>>      cgraph_inline_failed_t inline_failed;
>>>>>      struct bitpack_d bp;
>>>>>      int ecf_flags = 0;
>>>>> +  unsigned i, len;
>>>>>        caller = dyn_cast<cgraph_node *> (nodes[streamer_read_hwi (ib)]);
>>>>>      if (caller == NULL || caller->decl == NULL_TREE)
>>>>> @@ -1457,6 +1480,7 @@ input_edge (class lto_input_block *ib, 
>>>>> vec<symtab_node *> nodes,
>>>>>      bp = streamer_read_bitpack (ib);
>>>>>      inline_failed = bp_unpack_enum (&bp, cgraph_inline_failed_t, 
>>>>> CIF_N_REASONS);
>>>>>      stmt_id = bp_unpack_var_len_unsigned (&bp);
>>>>> +  speculative_id = bp_unpack_var_len_unsigned (&bp);
>>>>>        if (indirect)
>>>>>        edge = caller->create_indirect_edge (NULL, 0, count);
>>>>> @@ -1466,6 +1490,7 @@ input_edge (class lto_input_block *ib, 
>>>>> vec<symtab_node *> nodes,
>>>>>      edge->indirect_inlining_edge = bp_unpack_value (&bp, 1);
>>>>>      edge->speculative = bp_unpack_value (&bp, 1);
>>>>>      edge->lto_stmt_uid = stmt_id;
>>>>> +  edge->speculative_id = speculative_id;
>>>>>      edge->inline_failed = inline_failed;
>>>>>      edge->call_stmt_cannot_inline_p = bp_unpack_value (&bp, 1);
>>>>>      edge->can_throw_external = bp_unpack_value (&bp, 1);
>>>>> @@ -1485,9 +1510,21 @@ input_edge (class lto_input_block *ib, 
>>>>> vec<symtab_node *> nodes,
>>>>>          if (bp_unpack_value (&bp, 1))
>>>>>        ecf_flags |= ECF_RETURNS_TWICE;
>>>>>          edge->indirect_info->ecf_flags = ecf_flags;
>>>>> -      edge->indirect_info->common_target_id = streamer_read_hwi (ib);
>>>>> -      if (edge->indirect_info->common_target_id)
>>>>> -        edge->indirect_info->common_target_probability = 
>>>>> streamer_read_hwi (ib);
>>>>> +
>>>>> +      len = streamer_read_hwi (ib);
>>>>> +
>>>>> +      gcc_assert (len <= GCOV_TOPN_VALUES);
>>>>> +
>>>>> +      if (len)
>>>>> +    {
>>>>> +      indirect_target_info item;
>>>>> +      for (i = 0; i < len; i++)
>>>>> +        {
>>>>> +          item.common_target_id = streamer_read_hwi (ib);
>>>>> +          item.common_target_probability = streamer_read_hwi (ib);
>>>>> +          vec_safe_push (edge->indirect_info->indirect_call_targets, 
>>>>> item);
>>>>> +        }
>>>>> +    }
>>>>>        }
>>>>>    }
>>>>>    diff --git a/gcc/predict.c b/gcc/predict.c
>>>>> index 915f0806b11..3f56fa3a74a 100644
>>>>> --- a/gcc/predict.c
>>>>> +++ b/gcc/predict.c
>>>>> @@ -762,7 +762,6 @@ dump_prediction (FILE *file, enum br_predictor 
>>>>> predictor, int probability,
>>>>>          && bb->count.precise_p ()
>>>>>          && reason == REASON_NONE)
>>>>>        {
>>>>> -      gcc_assert (e->count ().precise_p ());
>>>>>          fprintf (file, ";;heuristics;%s;%" PRId64 ";%" PRId64 
>>>>> ";%.1f;\n",
>>>>>               predictor_info[predictor].name,
>>>>>               bb->count.to_gcov_type (), e->count ().to_gcov_type (),
>>>>> diff --git a/gcc/symtab.c b/gcc/symtab.c
>>>>> index ee9723c3453..d4c36fd3e5a 100644
>>>>> --- a/gcc/symtab.c
>>>>> +++ b/gcc/symtab.c
>>>>> @@ -603,6 +603,7 @@ symtab_node::create_reference (symtab_node 
>>>>> *referred_node,
>>>>>      ref->referred = referred_node;
>>>>>      ref->stmt = stmt;
>>>>>      ref->lto_stmt_uid = 0;
>>>>> +  ref->speculative_id = 0;
>>>>>      ref->use = use_type;
>>>>>      ref->speculative = 0;
>>>>>    @@ -660,6 +661,7 @@ symtab_node::clone_references (symtab_node *node)
>>>>>          ref2 = create_reference (ref->referred, ref->use, ref->stmt);
>>>>>          ref2->speculative = speculative;
>>>>>          ref2->lto_stmt_uid = stmt_uid;
>>>>> +      ref2->speculative_id = ref->speculative_id;
>>>>>        }
>>>>>    }
>>>>>    @@ -678,6 +680,7 @@ symtab_node::clone_referring (symtab_node *node)
>>>>>          ref2 = ref->referring->create_reference (this, ref->use, 
>>>>> ref->stmt);
>>>>>          ref2->speculative = speculative;
>>>>>          ref2->lto_stmt_uid = stmt_uid;
>>>>> +      ref2->speculative_id = ref->speculative_id;
>>>>>        }
>>>>>    }
>>>>>    @@ -693,6 +696,7 @@ symtab_node::clone_reference (ipa_ref *ref, 
>>>>> gimple *stmt)
>>>>>      ref2 = create_reference (ref->referred, ref->use, stmt);
>>>>>      ref2->speculative = speculative;
>>>>>      ref2->lto_stmt_uid = stmt_uid;
>>>>> +  ref2->speculative_id = ref->speculative_id;
>>>>>      return ref2;
>>>>>    }
>>>>>    @@ -747,6 +751,7 @@ symtab_node::clear_stmts_in_references (void)
>>>>>          {
>>>>>        r->stmt = NULL;
>>>>>        r->lto_stmt_uid = 0;
>>>>> +    r->speculative_id = 0;
>>>>>          }
>>>>>    }
>>>>>    diff --git 
>>>>> a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c 
>>>>> b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
>>>>> new file mode 100644
>>>>> index 00000000000..e0a83c2e067
>>>>> --- /dev/null
>>>>> +++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
>>>>> @@ -0,0 +1,35 @@
>>>>> +/* { dg-require-effective-target lto } */
>>>>> +/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
>>>>> +/* { dg-require-profiling "-fprofile-generate" } */
>>>>> +/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate 
>>>>> --param indir-call-topn-profile=1" } */
>>>>> +
>>>>> +#include <stdio.h>
>>>>> +
>>>>> +typedef int (*fptr) (int);
>>>>> +int
>>>>> +one (int a);
>>>>> +
>>>>> +int
>>>>> +two (int a);
>>>>> +
>>>>> +fptr table[] = {&one, &two};
>>>>> +
>>>>> +int
>>>>> +main()
>>>>> +{
>>>>> +  int i, x;
>>>>> +  fptr p = &one;
>>>>> +
>>>>> +  x = one (3);
>>>>> +
>>>>> +  for (i = 0; i < 350000000; i++)
>>>>> +    {
>>>>> +      x = (*p) (3);
>>>>> +      p = table[x];
>>>>> +    }
>>>>> +  printf ("done:%d\n", x);
>>>>> +}
>>>>> +
>>>>> +/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> 
>>>>> direct call.* one transformation on insn" "profile_estimate" } } */
>>>>> +/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> 
>>>>> direct call.* two transformation on insn" "profile_estimate" } } */
>>>>> +
>>>>> diff --git 
>>>>> a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c 
>>>>> b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
>>>>> new file mode 100644
>>>>> index 00000000000..a8c6e365fb9
>>>>> --- /dev/null
>>>>> +++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
>>>>> @@ -0,0 +1,22 @@
>>>>> +/* It seems there is no way to avoid the other source of mulitple
>>>>> +   source testcase from being compiled independently.  Just avoid
>>>>> +   error.  */
>>>>> +#ifdef DOJOB
>>>>> +int
>>>>> +one (int a)
>>>>> +{
>>>>> +  return 1;
>>>>> +}
>>>>> +
>>>>> +int
>>>>> +two (int a)
>>>>> +{
>>>>> +  return 0;
>>>>> +}
>>>>> +#else
>>>>> +int
>>>>> +main()
>>>>> +{
>>>>> +  return 0;
>>>>> +}
>>>>> +#endif
>>>>> diff --git 
>>>>> a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c 
>>>>> b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
>>>>> new file mode 100644
>>>>> index 00000000000..aa3887fde83
>>>>> --- /dev/null
>>>>> +++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
>>>>> @@ -0,0 +1,42 @@
>>>>> +/* { dg-require-effective-target lto } */
>>>>> +/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
>>>>> +/* { dg-require-profiling "-fprofile-generate" } */
>>>>> +/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate 
>>>>> --param indir-call-topn-profile=1" } */
>>>>> +
>>>>> +#include <stdio.h>
>>>>> +
>>>>> +typedef int (*fptr) (int);
>>>>> +int
>>>>> +one (int a);
>>>>> +
>>>>> +int
>>>>> +two (int a);
>>>>> +
>>>>> +fptr table[] = {&one, &two};
>>>>> +
>>>>> +int foo ()
>>>>> +{
>>>>> +  int i, x;
>>>>> +  fptr p = &one;
>>>>> +
>>>>> +  x = one (3);
>>>>> +
>>>>> +  for (i = 0; i < 350000000; i++)
>>>>> +    {
>>>>> +      x = (*p) (3);
>>>>> +      p = table[x];
>>>>> +    }
>>>>> +  return x;
>>>>> +}
>>>>> +
>>>>> +int
>>>>> +main()
>>>>> +{
>>>>> +  int x = foo ();
>>>>> +  printf ("done:%d\n", x);
>>>>> +}
>>>>> +
>>>>> +/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> 
>>>>> direct call.* one transformation on insn" "profile_estimate" } } */
>>>>> +/* { dg-final-use-not-autofdo { scan-wpa-ipa-dump "Indirect call -> 
>>>>> direct call.* two transformation on insn" "profile_estimate" } } */
>>>>> +
>>>>> +
>>>>> diff --git a/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c 
>>>>> b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
>>>>> new file mode 100644
>>>>> index 00000000000..951bc7ddd19
>>>>> --- /dev/null
>>>>> +++ b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
>>>>> @@ -0,0 +1,38 @@
>>>>> +/* { dg-require-profiling "-fprofile-generate" } */
>>>>> +/* { dg-options "-O2 -fdump-ipa-profile --param 
>>>>> indir-call-topn-profile=1" } */
>>>>> +
>>>>> +#include <stdio.h>
>>>>> +
>>>>> +typedef int (*fptr) (int);
>>>>> +int
>>>>> +one (int a)
>>>>> +{
>>>>> +  return 1;
>>>>> +}
>>>>> +
>>>>> +int
>>>>> +two (int a)
>>>>> +{
>>>>> +  return 0;
>>>>> +}
>>>>> +
>>>>> +fptr table[] = {&one, &two};
>>>>> +
>>>>> +int
>>>>> +main()
>>>>> +{
>>>>> +  int i, x;
>>>>> +  fptr p = &one;
>>>>> +
>>>>> +  one (3);
>>>>> +
>>>>> +  for (i = 0; i < 350000000; i++)
>>>>> +    {
>>>>> +      x = (*p) (3);
>>>>> +      p = table[x];
>>>>> +    }
>>>>> +  printf ("done:%d\n", x);
>>>>> +}
>>>>> +
>>>>> +/* { dg-final-use-not-autofdo { scan-ipa-dump "Indirect call -> 
>>>>> direct call.* one transformation on insn" "profile" } } */
>>>>> +/* { dg-final-use-not-autofdo { scan-ipa-dump "Indirect call -> 
>>>>> direct call.* two transformation on insn" "profile" } } */
>>>>> diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
>>>>> index b9c1a3b1456..fe3e172fbd1 100644
>>>>> --- a/gcc/tree-inline.c
>>>>> +++ b/gcc/tree-inline.c
>>>>> @@ -2167,6 +2167,25 @@ copy_bb (copy_body_data *id, basic_block bb,
>>>>>                    gcc_assert (!edge->indirect_unknown_callee);
>>>>>                  old_edge->speculative_call_info (direct, indirect, ref);
>>>>> +              while (old_edge->next_callee
>>>>> +                 && old_edge->next_callee->speculative
>>>>> +                 && indirect->has_multiple_indirect_call_p ())
>>>>> +                {
>>>>> +                  /* Some speculative calls may contain more than
>>>>> +                 one direct target, loop iterate it to clone all
>>>>> +                 related direct edges before cloning the related
>>>>> +                 indirect edge.  */
>>>>> +                  id->dst_node->clone_reference (ref, stmt);
>>>>> +
>>>>> +                  edge = old_edge->next_callee;
>>>>> +                  edge = edge->clone (id->dst_node, call_stmt,
>>>>> +                          gimple_uid (stmt), num, den,
>>>>> +                          true);
>>>>> +                  old_edge = old_edge->next_callee;
>>>>> +                  gcc_assert (!edge->indirect_unknown_callee);
>>>>> +                  old_edge->speculative_call_info (direct, indirect,
>>>>> +                                   ref);
>>>>> +                }
>>>>>                    profile_count indir_cnt = indirect->count;
>>>>>                  indirect = indirect->clone (id->dst_node, call_stmt,
>>>>> diff --git a/gcc/tree-profile.c b/gcc/tree-profile.c
>>>>> index 4c1ead5781f..ef7748668f8 100644
>>>>> --- a/gcc/tree-profile.c
>>>>> +++ b/gcc/tree-profile.c
>>>>> @@ -74,8 +74,8 @@ static GTY(()) tree ic_tuple_callee_field;
>>>>>    /* Do initialization work for the edge profiler.  */
>>>>>      /* Add code:
>>>>> -   __thread gcov*    __gcov_indirect_call_counters; // pointer to 
>>>>> actual counter
>>>>> -   __thread void*    __gcov_indirect_call_callee; // actual callee 
>>>>> address
>>>>> +   __thread gcov*    __gcov_indirect_call.counters; // pointer to 
>>>>> actual counter
>>>>> +   __thread void*    __gcov_indirect_call.callee; // actual callee 
>>>>> address
>>>>>       __thread int __gcov_function_counter; // time profiler function 
>>>>> counter
>>>>>    */
>>>>>    static void
>>>>> @@ -382,7 +382,7 @@ gimple_gen_ic_profiler (histogram_value value, 
>>>>> unsigned tag)
>>>>>          f_1 = foo;
>>>>>          __gcov_indirect_call.counters = &__gcov4.main[0];
>>>>>          PROF_9 = f_1;
>>>>> -      __gcov_indirect_call_callee = PROF_9;
>>>>> +      __gcov_indirect_call.callee = PROF_9;
>>>>>          _4 = f_1 ();
>>>>>       */
>>>>>    @@ -445,11 +445,11 @@ gimple_gen_ic_func_profiler (void)
>>>>>        /* Insert code:
>>>>>    -     if (__gcov_indirect_call_callee != NULL)
>>>>> +     if (__gcov_indirect_call.callee != NULL)
>>>>>           __gcov_indirect_call_profiler_v3 (profile_id, 
>>>>> &current_function_decl);
>>>>>           The function __gcov_indirect_call_profiler_v3 is responsible 
>>>>> for
>>>>> -     resetting __gcov_indirect_call_callee to NULL.  */
>>>>> +     resetting __gcov_indirect_call.callee to NULL.  */
>>>>>        gimple_stmt_iterator gsi = gsi_start_bb (cond_bb);
>>>>>      void0 = build_int_cst (ptr_type_node, 0);
>>>>> @@ -891,7 +891,7 @@ pass_ipa_tree_profile::gate (function *)
>>>>>    {
>>>>>      /* When profile instrumentation, use or test coverage shall be 
>>>>> performed.
>>>>>         But for AutoFDO, this there is no instrumentation, thus this 
>>>>> pass is
>>>>> -     diabled.  */
>>>>> +     disabled.  */
>>>>>      return (!in_lto_p && !flag_auto_profile
>>>>>          && (flag_branch_probabilities || flag_test_coverage
>>>>>              || profile_arc_flag));
>>>>> diff --git a/gcc/value-prof.c b/gcc/value-prof.c
>>>>> index 55ea0973a03..0588df0fce9 100644
>>>>> --- a/gcc/value-prof.c
>>>>> +++ b/gcc/value-prof.c
>>>>> @@ -1406,11 +1406,10 @@ gimple_ic (gcall *icall_stmt, struct 
>>>>> cgraph_node *direct_call,
>>>>>      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:
>>>>> - */
>>>>> +/* There maybe multiple indirect targets in histogram.  Check every
>>>>> +   indirect/virtual call if callee function exists, if not exist, 
>>>>> leave it to
>>>>> +   LTO stage for later process.  Modify code of this indirect call to 
>>>>> an if-else
>>>>> +   structure in ipa-profile finally.  */
>>>>>      static bool
>>>>>    gimple_ic_transform (gimple_stmt_iterator *gsi)
>>>>> @@ -1434,48 +1433,57 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
>>>>>      if (!histogram)
>>>>>        return false;
>>>>
>>>> The function is not correct, note that the function can now return true
>>>> when this transformation happens:
>>>>            "Indirect call -> direct call from other "
>>>>            "module %T=> %i (will resolve only with LTO)\n",
>>>>
>>>> Current trunk returns false in that case.
>>>>
>>>>>    -  if (!get_nth_most_common_value (NULL, "indirect call", 
>>>>> histogram, &val,
>>>>> -                  &count, &all))
>>>>> -    return false;
>>>>> +  count = 0;
>>>>> +  all = histogram->hvalue.counters[0];
>>>>>    -  if (4 * count <= 3 * all)
>>>>> -    return false;
>>>>> +  for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
>>>>> +    {
>>>>> +      if (!get_nth_most_common_value (NULL, "indirect call", 
>>>>> histogram, &val,
>>>>> +                      &count, &all, j))
>>>>> +    continue;
>>>>
>>>> You should break here as get_nth_most_common_value (..., j + 1) will 
>>>> also return
>>>> false.
>>>>
>>>>>    -  direct_call = find_func_by_profile_id ((int)val);
>>>>> +      /* Minimum probability.  should be higher than 25%.  */
>>>>> +      if (4 * count <= all)
>>>>> +    continue;
>>>>
>>>> You can break here as well.
>>>>
>>>> Thank you,
>>>> Martin
>>>>
>>>>>    -  if (direct_call == NULL)
>>>>> -    {
>>>>> -      if (val)
>>>>> +      direct_call = find_func_by_profile_id ((int) val);
>>>>> +
>>>>> +      if (direct_call == NULL)
>>>>> +    {
>>>>> +      if (val)
>>>>> +        {
>>>>> +          if (dump_enabled_p ())
>>>>> +        dump_printf_loc (
>>>>> +          MSG_MISSED_OPTIMIZATION, stmt,
>>>>> +          "Indirect call -> direct call from other "
>>>>> +          "module %T=> %i (will resolve only with LTO)\n",
>>>>> +          gimple_call_fn (stmt), (int) val);
>>>>> +        }
>>>>> +      continue;
>>>>> +    }
>>>>> +
>>>>> +      if (!check_ic_target (stmt, direct_call))
>>>>>        {
>>>>>          if (dump_enabled_p ())
>>>>> -        dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
>>>>> -                 "Indirect call -> direct call from other "
>>>>> -                 "module %T=> %i (will resolve only with LTO)\n",
>>>>> -                 gimple_call_fn (stmt), (int)val);
>>>>> +        dump_printf_loc (
>>>>> +          MSG_MISSED_OPTIMIZATION, stmt,
>>>>> +          "Indirect call -> direct call %T => %T "
>>>>> +          "transformation skipped because of type mismatch: %G",
>>>>> +          gimple_call_fn (stmt), direct_call->decl, stmt);
>>>>> +      gimple_remove_histogram_value (cfun, stmt, histogram);
>>>>> +      return false;
>>>>>        }
>>>>> -      return false;
>>>>> -    }
>>>>>    -  if (!check_ic_target (stmt, direct_call))
>>>>> -    {
>>>>>          if (dump_enabled_p ())
>>>>> -    dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
>>>>> -             "Indirect call -> direct call %T => %T "
>>>>> -             "transformation skipped because of type mismatch: %G",
>>>>> -             gimple_call_fn (stmt), direct_call->decl, stmt);
>>>>> -      gimple_remove_histogram_value (cfun, stmt, histogram);
>>>>> -      return false;
>>>>> -    }
>>>>> -
>>>>> -  if (dump_enabled_p ())
>>>>> -    {
>>>>> -      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
>>>>> -               "Indirect call -> direct call "
>>>>> -               "%T => %T transformation on insn postponed\n",
>>>>> -               gimple_call_fn (stmt), direct_call->decl);
>>>>> -      dump_printf_loc (MSG_NOTE, stmt,
>>>>> -               "hist->count %" PRId64
>>>>> -               " hist->all %" PRId64"\n", count, all);
>>>>> +    {
>>>>> +      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
>>>>> +               "Indirect call -> direct call "
>>>>> +               "%T => %T transformation on insn postponed\n",
>>>>> +               gimple_call_fn (stmt), direct_call->decl);
>>>>> +      dump_printf_loc (MSG_NOTE, stmt,
>>>>> +               "hist->count %" PRId64 " hist->all %" PRId64 "\n",
>>>>> +               count, all);
>>>>> +    }
>>>>>        }
>>>>>        return true;
>>>>>
>>>>
>>

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v5] Missed function specialization + partial devirtualization
  2019-09-27  7:13               ` [PATCH v5] " luoxhu
  2019-10-16  3:33                 ` Ping: " luoxhu
@ 2019-10-22 14:24                 ` Martin Liška
  2019-11-06  5:06                   ` Ping: " luoxhu
  1 sibling, 1 reply; 38+ messages in thread
From: Martin Liška @ 2019-10-22 14:24 UTC (permalink / raw)
  To: luoxhu, gcc-patches; +Cc: hubicka, segher, wschmidt

[-- Attachment #1: Type: text/plain, Size: 283 bytes --]

On 9/27/19 9:13 AM, luoxhu wrote:
> Thanks for your time of so many round of reviews.

You're welcome. One last request would be please to make
gimple_ic_transform a void function. See attached patch.

I'll remind the patch today to Honza.

Thanks,
Martin

[-- Attachment #2: value-prof.patch --]
[-- Type: text/x-patch, Size: 3197 bytes --]

diff --git a/gcc/value-prof.c b/gcc/value-prof.c
index 5f095d848f0..7cb99376c69 100644
--- a/gcc/value-prof.c
+++ b/gcc/value-prof.c
@@ -107,7 +107,7 @@ static bool gimple_divmod_fixed_value_transform (gimple_stmt_iterator *);
 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 gimple_ic_transform (gimple_stmt_iterator *);
 
 /* Allocate histogram value.  */
 
@@ -617,8 +617,7 @@ gimple_value_profile_transformations (void)
 	  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;
@@ -629,6 +628,9 @@ gimple_value_profile_transformations (void)
 		  gsi = gsi_for_stmt (stmt);
 		}
 	    }
+
+	  /* The function never thansforms a GIMPLE statement.  */
+	  gimple_ic_transform (&gsi);
         }
     }
 
@@ -1411,7 +1413,7 @@ gimple_ic (gcall *icall_stmt, struct cgraph_node *direct_call,
    LTO stage for later process.  Modify code of this indirect call to an if-else
    structure in ipa-profile finally.  */
 
-static bool
+static void
 gimple_ic_transform (gimple_stmt_iterator *gsi)
 {
   gcall *stmt;
@@ -1421,17 +1423,17 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
 
   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;
+    return;
 
   count = 0;
   all = histogram->hvalue.counters[0];
@@ -1440,11 +1442,11 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
     {
       if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
 				      &count, &all, j))
-	return false;
+	return;
 
       /* Minimum probability.  should be higher than 25%.  */
       if (4 * count <= all)
-	return false;
+	return;
 
       direct_call = find_func_by_profile_id ((int) val);
 
@@ -1459,7 +1461,7 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
 		  "module %T=> %i (will resolve only with LTO)\n",
 		  gimple_call_fn (stmt), (int) val);
 	    }
-	  return false;
+	  return;
 	}
 
       if (!check_ic_target (stmt, direct_call))
@@ -1471,7 +1473,7 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
 	      "transformation skipped because of type mismatch: %G",
 	      gimple_call_fn (stmt), direct_call->decl, stmt);
 	  gimple_remove_histogram_value (cfun, stmt, histogram);
-	  return false;
+	  return;
 	}
 
       if (dump_enabled_p ())
@@ -1485,8 +1487,6 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
 			   count, all);
 	}
     }
-
-  return false;
 }
 
 /* Return true if the stringop CALL shall be profiled.  SIZE_ARG be

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Ping: [PATCH v5] Missed function specialization + partial devirtualization
  2019-10-22 14:24                 ` Martin Liška
@ 2019-11-06  5:06                   ` luoxhu
  2019-11-14  6:01                     ` Ping*2: " luoxhu
  0 siblings, 1 reply; 38+ messages in thread
From: luoxhu @ 2019-11-06  5:06 UTC (permalink / raw)
  To: Martin Liška, gcc-patches; +Cc: hubicka, segher, wschmidt

On 2019/10/22 22:07, Martin Liška wrote:
> On 9/27/19 9:13 AM, luoxhu wrote:
>> Thanks for your time of so many round of reviews.
> 
> You're welcome. One last request would be please to make
> gimple_ic_transform a void function. See attached patch.
> 
> I'll remind the patch today to Honza.

Thanks,  Martin.  Will add your changes:).  Ping again~


Xiong Hu
BR

> 
> Thanks,
> Martin
> 

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Ping*2: [PATCH v5] Missed function specialization + partial devirtualization
  2019-11-06  5:06                   ` Ping: " luoxhu
@ 2019-11-14  6:01                     ` luoxhu
  2019-11-14  9:15                       ` Jan Hubicka
  0 siblings, 1 reply; 38+ messages in thread
From: luoxhu @ 2019-11-14  6:01 UTC (permalink / raw)
  To: Martin Liška, gcc-patches; +Cc: hubicka, segher, wschmidt

Rebase to trunk including void gimple_ic_transform.


This patch aims to fix PR69678 caused by PGO indirect call profiling
performance issues.
The bug that profiling data is never working was fixed by Martin's pull
back of topN patches, performance got GEOMEAN ~1% improvement(+24% for
511.povray_r specifically).
Still, currently the default profile only generates SINGLE indirect target
that called more than 75%.  This patch leverages MULTIPLE indirect
targets use in LTO-WPA and LTO-LTRANS stage, as a result, function
specialization, profiling, partial devirtualization, inlining and
cloning could be done successfully based on it.
Performance can get improved from 0.70 sec to 0.38 sec on simple tests.
Details are:
  1.  PGO with topn is enabled by default now, but only one indirect
  target edge will be generated in ipa-profile pass, so add variables to enable
  multiple speculative edges through passes, speculative_id will record the
  direct edge index bind to the indirect edge, indirect_call_targets length
  records how many direct edges owned by the indirect edge, postpone gimple_ic
  to ipa-profile like default as inline pass will decide whether it is benefit
  to transform indirect call.
  2.  Use speculative_id to track and search the reference node matched
  with the direct edge's callee for multiple targets.  Actually, it is the
  caller's responsibility to handle the direct edges mapped to same indirect
  edge.  speculative_call_info will return one of the direct edge specified,
  this will leverage current IPA edge process framework mostly.
  3.  Enable LTO WPA/LTRANS stage multiple indirect call targets analysis for
  profile full support in ipa passes and cgraph_edge functions.  speculative_id
  can be set by make_speculative id when multiple targets are binded to
  one indirect edge, and cloned if new edge is cloned.  speculative_id
  is streamed out and stream int by lto like lto_stmt_uid.
  4.  Add 1 in module testcase and 2 cross module testcases.
  5.  Bootstrap and regression test passed on Power8-LE.  No function
  and performance regression for SPEC2017.

gcc/ChangeLog

	2019-11-14  Xiong Hu Luo  <luoxhu@linux.ibm.com>

	PR ipa/69678
	* cgraph.c (symbol_table::create_edge): Init speculative_id.
	(cgraph_edge::make_speculative): Add param for setting speculative_id.
	(cgraph_edge::speculative_call_info): Find reference by
	speculative_id for multiple indirect targets.
	(cgraph_edge::resolve_speculation): Decrease the speculations
	for indirect edge, drop it's speculative if not direct target
	left.
	(cgraph_edge::redirect_call_stmt_to_callee): Likewise.
	(cgraph_node::verify_node): Don't report error if speculative
	edge not include statement.
	(cgraph_edge::has_multiple_indirect_call_p): New function.
	(cgraph_edge::has_indirect_call_p): New function.
	* cgraph.h (struct indirect_target_info): New struct.
	(indirect_call_targets): New vector variable.
	(make_speculative): Add param for setting speculative_id.
	(cgraph_edge::has_multiple_indirect_call_p): New declare.
	(cgraph_edge::has_indirect_call_p): New declare.
	(speculative_id): New variable.
	* cgraphclones.c (cgraph_node::create_clone): Clone speculative_id.
	* cgraphunit.c: Fix comments typo.
	* ipa-comdats.c: Fix comments typo.
	* ipa-inline.c (inline_small_functions): Fix iterator update.
	* ipa-profile.c (ipa_profile_generate_summary): Add indirect
	multiple targets logic.
	(ipa_profile): Likewise.
	* ipa-ref.h (speculative_id): New variable.
	* ipa.c (process_references): Fix typo.
	* lto-cgraph.c (lto_output_edge): Add indirect multiple targets
	logic.  Stream out speculative_id.
	(input_edge): Likewise.
	* predict.c (dump_prediction): Revome edges count assert to be
	precise.
	* symtab.c (symtab_node::create_reference): Init speculative_id.
	(symtab_node::clone_references): Clone speculative_id.
	(symtab_node::clone_referring): Clone speculative_id.
	(symtab_node::clone_reference): Clone speculative_id.
	(symtab_node::clear_stmts_in_references): Clear speculative_id.
	* tree-inline.c (copy_bb): Duplicate all the speculative edges
	if indirect call contains multiple speculative targets.
	* tree-profile.c (gimple_gen_ic_profiler): Use the new variable
	__gcov_indirect_call.counters and __gcov_indirect_call.callee.
	(gimple_gen_ic_func_profiler): Likewise.
	(pass_ipa_tree_profile::gate): Fix comment typos.
	* value-prof.h  (check_ic_target): Remove.
	* value-prof.c  (gimple_value_profile_transformations):
	Use void function gimple_ic_transform.
	* value-prof.c  (gimple_ic_transform): Handle topn case.
	Fix comment typos.  Change it to a void function.

gcc/testsuite/ChangeLog

	2019-11-14  Xiong Hu Luo  <luoxhu@linux.ibm.com>

	PR ipa/69678
	* gcc.dg/tree-prof/indir-call-prof-topn.c: New testcase.
	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c: New testcase.
	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c: New testcase.
	* gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c: New testcase.
	* lib/scandump.exp: Dump executable file name.
	* lib/scanwpaipa.exp: New scan-pgo-wap-ipa-dump.
---
 gcc/cgraph.c                                  | 90 +++++++++++++++++-
 gcc/cgraph.h                                  | 34 ++++++-
 gcc/cgraphclones.c                            |  1 +
 gcc/ipa-comdats.c                             |  2 +-
 gcc/ipa-inline.c                              | 15 +--
 gcc/ipa-profile.c                             | 95 ++++++++++++++-----
 gcc/ipa-ref.h                                 |  1 +
 gcc/ipa.c                                     |  2 +-
 gcc/lto-cgraph.c                              | 56 +++++++++--
 gcc/predict.c                                 |  1 -
 gcc/symtab.c                                  |  5 +
 .../tree-prof/crossmodule-indir-call-topn-1.c | 33 +++++++
 .../crossmodule-indir-call-topn-1a.c          | 22 +++++
 .../tree-prof/crossmodule-indir-call-topn-2.c | 40 ++++++++
 .../gcc.dg/tree-prof/indir-call-prof-topn.c   | 37 ++++++++
 gcc/testsuite/lib/scandump.exp                |  1 +
 gcc/testsuite/lib/scanwpaipa.exp              | 23 +++++
 gcc/tree-inline.c                             | 19 ++++
 gcc/tree-profile.c                            | 12 +--
 gcc/value-prof.c                              | 87 +++++++++--------
 gcc/value-prof.h                              |  1 -
 21 files changed, 475 insertions(+), 102 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c

diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index 1f7a5c58d98..16e92267152 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -864,6 +864,7 @@ symbol_table::create_edge (cgraph_node *caller, cgraph_node *callee,
   edge->prev_callee = NULL;
   edge->next_callee = NULL;
   edge->lto_stmt_uid = 0;
+  edge->speculative_id = 0;
 
   edge->count = count;
   edge->call_stmt = call_stmt;
@@ -1053,7 +1054,8 @@ cgraph_edge::remove (void)
    Return direct edge created.  */
 
 cgraph_edge *
-cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
+cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count,
+			       unsigned int speculative_id)
 {
   cgraph_node *n = caller;
   ipa_ref *ref = NULL;
@@ -1071,11 +1073,13 @@ cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
   else
     e2->can_throw_external = can_throw_external;
   e2->lto_stmt_uid = lto_stmt_uid;
+  e2->speculative_id = speculative_id;
   e2->in_polymorphic_cdtor = in_polymorphic_cdtor;
   count -= e2->count;
   symtab->call_edge_duplication_hooks (this, e2);
   ref = n->create_reference (n2, IPA_REF_ADDR, call_stmt);
   ref->lto_stmt_uid = lto_stmt_uid;
+  ref->speculative_id = speculative_id;
   ref->speculative = speculative;
   n2->mark_address_taken ();
   return e2;
@@ -1089,6 +1093,38 @@ cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
    call) and if one of them exists, all of them must exist.
 
    Given speculative call edge, return all three components.
+
+   For some indirect edge, it may maps to multiple direct edges, i.e.  1:N.
+   check the speculative_id to return all the three components for specified
+   direct edge or indirect edge.
+   If input is indirect, caller of this function will get the direct edge one by
+   one, get_edge will just return one of the direct edge mapped to the indirect
+   edge, the returned direct edge will be resolved or redirected by the caller,
+   then number of indirect calls (speculations) is deceased in each access.
+   If input is direct, this function will get the indirect edge and reference
+   with matched speculative_id, the returned edge will also be resolved or
+   redirected, decrease the speculations accordingly.
+   Speculations of indirect edge will be dropped only if all direct edges
+   be handled.
+
+   e.g.  for indirect edge E statement "call call_dest":
+
+   Redirect N3 after redirected N2:
+
+   if (call_dest == N2)
+     n2 ();
+   else if (call_dest == N3)
+     n3 ();
+   else
+     call call_dest
+
+   Resolve N3 and only redirect N2:
+
+   if (call_dest == N2)
+     n2 ();
+   else
+     call call_dest
+
  */
 
 void
@@ -1128,7 +1164,7 @@ cgraph_edge::speculative_call_info (cgraph_edge *&direct,
 
   reference = NULL;
   for (i = 0; e->caller->iterate_reference (i, ref); i++)
-    if (ref->speculative
+    if (ref->speculative && ref->speculative_id == e->speculative_id
 	&& ((ref->stmt && ref->stmt == e->call_stmt)
 	    || (!ref->stmt && ref->lto_stmt_uid == e->lto_stmt_uid)))
       {
@@ -1189,7 +1225,21 @@ cgraph_edge::resolve_speculation (tree callee_decl)
          in the functions inlined through it.  */
     }
   edge->count += e2->count;
-  edge->speculative = false;
+  /* edge is indirect, e2 is direct.  If edge contains multiple speculations,
+     remove one of speculations for this indirect edge, then if edge still
+     contains direct target, keep the speculation, next direct target
+     will continue use it.  Give up speculation completely if no direct
+     target is left for this indirect edge.  */
+  if (edge->has_indirect_call_p ())
+    {
+      /* As the direct targets are sorted by decrease, delete the first target
+	 when it is resolved.  */
+      edge->indirect_info->indirect_call_targets->ordered_remove (0);
+      if (edge->indirect_info->indirect_call_targets->is_empty ())
+	edge->speculative = false;
+    }
+  else
+    edge->speculative = false;
   e2->speculative = false;
   ref->remove_reference ();
   if (e2->indirect_unknown_callee || e2->inline_failed)
@@ -1297,7 +1347,21 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
 	  e->caller->set_call_stmt_including_clones (e->call_stmt, new_stmt,
 						     false);
 	  e->count = gimple_bb (e->call_stmt)->count;
-	  e2->speculative = false;
+	  /* edge is direct, e2 is indirect here.  If e2 contains multiple
+	     speculations, remove one of speculations for this indirect edge,
+	     then if e2 still contains direct target, keep the speculation,
+	     next direct target will continue use it.  Give up speculation
+	     completely if no direct target is left for this indirect e2.  */
+	  if (e2->has_indirect_call_p ())
+	    {
+	      /* As the direct targets are sorted by decrease, delete the first
+		 target when it is redirected.  */
+	      e2->indirect_info->indirect_call_targets->ordered_remove (0);
+	      if (e2->indirect_info->indirect_call_targets->is_empty ())
+		e2->speculative = false;
+	    }
+	  else
+	    e2->speculative = false;
 	  e2->count = gimple_bb (e2->call_stmt)->count;
 	  ref->speculative = false;
 	  ref->stmt = NULL;
@@ -3359,7 +3423,7 @@ cgraph_node::verify_node (void)
 
       for (e = callees; e; e = e->next_callee)
 	{
-	  if (!e->aux)
+	  if (!e->aux && !e->speculative)
 	    {
 	      error ("edge %s->%s has no corresponding call_stmt",
 		     identifier_to_locale (e->caller->name ()),
@@ -3696,6 +3760,22 @@ cgraph_edge::possibly_call_in_translation_unit_p (void)
   return node->get_availability () >= AVAIL_INTERPOSABLE;
 }
 
+/* Return true if this edge has multiple indirect call targets.  */
+bool
+cgraph_edge::has_multiple_indirect_call_p (void)
+{
+  return (indirect_info && indirect_info->indirect_call_targets
+	  && indirect_info->indirect_call_targets->length () > 1);
+}
+
+/* Return true if this edge has at least one indirect call target.  */
+bool
+cgraph_edge::has_indirect_call_p (void)
+{
+  return (indirect_info && indirect_info->indirect_call_targets
+	  && !indirect_info->indirect_call_targets->is_empty ());
+}
+
 /* A stashed copy of "symtab" for use by selftest::symbol_table_test.
    This needs to be a global so that it can be a GC root, and thus
    prevent the stashed copy from being garbage-collected if the GC runs
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index a4f14743f00..1299702788d 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -1636,6 +1636,21 @@ private:
   void make_speculative (tree otr_type = NULL);
 };
 
+/* Structure containing indirect target information from profile.  */
+
+struct GTY (()) indirect_target_info
+{
+  indirect_target_info (unsigned int id, int prob)
+    : common_target_id (id), common_target_probability (prob)
+  {
+  }
+
+  /* Profile_id of common target obtained from profile.  */
+  unsigned int common_target_id;
+  /* Probability that call will land in function with COMMON_TARGET_ID.  */
+  int common_target_probability;
+};
+
 /* Structure containing additional information about an indirect call.  */
 
 class GTY(()) cgraph_indirect_call_info
@@ -1654,10 +1669,9 @@ public:
   int param_index;
   /* ECF flags determined from the caller.  */
   int ecf_flags;
-  /* Profile_id of common target obtained from profile.  */
-  int common_target_id;
-  /* Probability that call will land in function with COMMON_TARGET_ID.  */
-  int common_target_probability;
+
+  /* An indirect call may contain one or multiple call targets.  */
+  vec<indirect_target_info, va_gc> *indirect_call_targets;
 
   /* Set when the call is a virtual call with the parameter being the
      associated object pointer rather than a simple direct call.  */
@@ -1714,7 +1728,8 @@ public:
   /* Turn edge into speculative call calling N2. Update
      the profile so the direct call is taken COUNT times
      with FREQUENCY.  */
-  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count);
+  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count,
+				 unsigned int speculative_id = 0);
 
    /* Given speculative call edge, return all three components.  */
   void speculative_call_info (cgraph_edge *&direct, cgraph_edge *&indirect,
@@ -1773,6 +1788,12 @@ public:
      be internal to the current translation unit.  */
   bool possibly_call_in_translation_unit_p (void);
 
+  /* Return true if this edge has multiple indirect call targets.  */
+  bool has_multiple_indirect_call_p (void);
+
+  /* Return true if this edge has at least one indirect call target.  */
+  bool has_indirect_call_p (void);
+
   /* Expected number of executions: calculated in profile.c.  */
   profile_count count;
   cgraph_node *caller;
@@ -1792,6 +1813,9 @@ public:
   /* The stmt_uid of call_stmt.  This is used by LTO to recover the call_stmt
      when the function is serialized in.  */
   unsigned int lto_stmt_uid;
+  /* speculative id is used by multiple indirect targets when the function is
+   speculated.  */
+  unsigned int speculative_id;
   /* Whether this edge was made direct by indirect inlining.  */
   unsigned int indirect_inlining_edge : 1;
   /* Whether this edge describes an indirect call with an undetermined
diff --git a/gcc/cgraphclones.c b/gcc/cgraphclones.c
index bfcebb20495..4c8cb82bd20 100644
--- a/gcc/cgraphclones.c
+++ b/gcc/cgraphclones.c
@@ -128,6 +128,7 @@ cgraph_edge::clone (cgraph_node *n, gcall *call_stmt, unsigned stmt_uid,
   new_edge->inline_failed = inline_failed;
   new_edge->indirect_inlining_edge = indirect_inlining_edge;
   new_edge->lto_stmt_uid = stmt_uid;
+  new_edge->speculative_id = speculative_id;
   /* Clone flags that depend on call_stmt availability manually.  */
   new_edge->can_throw_external = can_throw_external;
   new_edge->call_stmt_cannot_inline_p = call_stmt_cannot_inline_p;
diff --git a/gcc/ipa-comdats.c b/gcc/ipa-comdats.c
index b496497ff66..0672d6d6575 100644
--- a/gcc/ipa-comdats.c
+++ b/gcc/ipa-comdats.c
@@ -18,7 +18,7 @@ along with GCC; see the file COPYING3.  If not see
 <http://www.gnu.org/licenses/>.  */
 
 /* This is very simple pass that looks for static symbols that are used
-   exlusively by symbol within one comdat group.  In this case it makes
+   exclusively by symbol within one comdat group.  In this case it makes
    sense to bring the symbol itself into the group to avoid dead code
    that would arrise when the comdat group from current unit is replaced
    by a different copy.  Consider for example:
diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
index 78ec0ec685f..8fc67ef88a5 100644
--- a/gcc/ipa-inline.c
+++ b/gcc/ipa-inline.c
@@ -1945,12 +1945,15 @@ inline_small_functions (void)
 	}
       if (has_speculative)
 	for (edge = node->callees; edge; edge = next)
-	  if (edge->speculative && !speculation_useful_p (edge,
-							  edge->aux != NULL))
-	    {
-	      edge->resolve_speculation ();
-	      update = true;
-	    }
+	  {
+	    next = edge->next_callee;
+	    if (edge->speculative
+		&& !speculation_useful_p (edge, edge->aux != NULL))
+	      {
+		edge->resolve_speculation ();
+		update = true;
+	      }
+	  }
       if (update)
 	{
 	  struct cgraph_node *where = node->inlined_to
diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
index 61fd5e86681..6f66ee1afd1 100644
--- a/gcc/ipa-profile.c
+++ b/gcc/ipa-profile.c
@@ -191,23 +191,34 @@ ipa_profile_generate_summary (void)
 		  if (h)
 		    {
 		      gcov_type val, count, all;
-		      if (get_nth_most_common_value (NULL, "indirect call", h,
-						     &val, &count, &all))
+		      struct cgraph_edge *e = node->get_edge (stmt);
+		      if (e && !e->indirect_unknown_callee)
+			continue;
+
+		      for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
 			{
-			  struct cgraph_edge * e = node->get_edge (stmt);
-			  if (e && !e->indirect_unknown_callee)
+			  if (!get_nth_most_common_value (NULL, "indirect call",
+							  h, &val, &count, &all,
+							  j))
+			    continue;
+
+			  if (val == 0)
 			    continue;
 
-			  e->indirect_info->common_target_id = val;
-			  e->indirect_info->common_target_probability
-			    = GCOV_COMPUTE_SCALE (count, all);
-			  if (e->indirect_info->common_target_probability > REG_BR_PROB_BASE)
+			  indirect_target_info item (val,
+						     GCOV_COMPUTE_SCALE (count,
+									 all));
+			  if (item.common_target_probability > REG_BR_PROB_BASE)
 			    {
 			      if (dump_file)
-				fprintf (dump_file, "Probability capped to 1\n");
-			      e->indirect_info->common_target_probability = REG_BR_PROB_BASE;
+				fprintf (dump_file,
+					 "Probability capped to 1\n");
+			      item.common_target_probability = REG_BR_PROB_BASE;
 			    }
+			  vec_safe_push (
+			    e->indirect_info->indirect_call_targets, item);
 			}
+
 		      gimple_remove_histogram_value (DECL_STRUCT_FUNCTION (node->decl),
 						      stmt, h);
 		    }
@@ -512,6 +523,7 @@ ipa_profile (void)
   int nindirect = 0, ncommon = 0, nunknown = 0, nuseless = 0, nconverted = 0;
   int nmismatch = 0, nimpossible = 0;
   bool node_map_initialized = false;
+  gcov_type threshold;
 
   if (dump_file)
     dump_histogram (dump_file, histogram);
@@ -520,14 +532,12 @@ ipa_profile (void)
       overall_time += histogram[i]->count * histogram[i]->time;
       overall_size += histogram[i]->size;
     }
+  threshold = 0;
   if (overall_time)
     {
-      gcov_type threshold;
-
       gcc_assert (overall_size);
 
       cutoff = (overall_time * param_hot_bb_count_ws_permille + 500) / 1000;
-      threshold = 0;
       for (i = 0; cumulated < cutoff; i++)
 	{
 	  cumulated += histogram[i]->count * histogram[i]->time;
@@ -563,7 +573,7 @@ ipa_profile (void)
   histogram.release ();
   histogram_pool.release ();
 
-  /* Produce speculative calls: we saved common traget from porfiling into
+  /* Produce speculative calls: we saved common target from profiling into
      e->common_target_id.  Now, at link time, we can look up corresponding
      function node and produce speculative call.  */
 
@@ -578,13 +588,37 @@ ipa_profile (void)
 	{
 	  if (n->count.initialized_p ())
 	    nindirect++;
-	  if (e->indirect_info->common_target_id)
+	  if (e->has_indirect_call_p ())
 	    {
 	      if (!node_map_initialized)
-	        init_node_map (false);
+		init_node_map (false);
 	      node_map_initialized = true;
 	      ncommon++;
-	      n2 = find_func_by_profile_id (e->indirect_info->common_target_id);
+
+	      if (in_lto_p)
+		{
+		  if (dump_file)
+		    {
+		      fprintf (dump_file,
+			       "Updating hotness threshold in LTO mode.\n");
+		      fprintf (dump_file, "Updated min count: %" PRId64 "\n",
+			       (int64_t) threshold);
+		    }
+		  set_hot_bb_threshold (threshold
+		    / e->indirect_info->indirect_call_targets->length ());
+		}
+
+	      unsigned speculative_id = 0;
+	      indirect_target_info *item;
+	      /* The code below is not formatted yet for review convenience.
+		 Move to a seprate small function is not easy as too many local
+		 variables used in it.  Need format and remove this comments
+		 once got approved.  */
+	      FOR_EACH_VEC_SAFE_ELT (e->indirect_info->indirect_call_targets, i,
+				     item)
+	       {
+	      bool speculative_found = false;
+	      n2 = find_func_by_profile_id (item->common_target_id);
 	      if (n2)
 		{
 		  if (dump_file)
@@ -593,11 +627,10 @@ ipa_profile (void)
 			       " other module %s => %s, prob %3.2f\n",
 			       n->dump_name (),
 			       n2->dump_name (),
-			       e->indirect_info->common_target_probability
-			       / (float)REG_BR_PROB_BASE);
+			       item->common_target_probability
+				 / (float) REG_BR_PROB_BASE);
 		    }
-		  if (e->indirect_info->common_target_probability
-		      < REG_BR_PROB_BASE / 2)
+		  if (item->common_target_probability < REG_BR_PROB_BASE / 2)
 		    {
 		      nuseless++;
 		      if (dump_file)
@@ -653,20 +686,30 @@ ipa_profile (void)
 			    n2 = alias;
 			}
 		      nconverted++;
-		      e->make_speculative
-			(n2,
-			 e->count.apply_probability
-				     (e->indirect_info->common_target_probability));
+		      e->make_speculative (n2,
+					   e->count.apply_probability (
+					     item->common_target_probability),
+					   speculative_id);
 		      update = true;
+		      speculative_id++;
+		      speculative_found = true;
 		    }
 		}
 	      else
 		{
 		  if (dump_file)
 		    fprintf (dump_file, "Function with profile-id %i not found.\n",
-			     e->indirect_info->common_target_id);
+			     item->common_target_id);
 		  nunknown++;
 		}
+	      if (!speculative_found)
+		{
+		  /* Remove item from indirect_call_targets if no
+		     speculative edge generated, rollback the iteration.  */
+		  e->indirect_info->indirect_call_targets->ordered_remove (i);
+		  i--;
+		}
+	       }
 	    }
 	 }
        if (update)
diff --git a/gcc/ipa-ref.h b/gcc/ipa-ref.h
index 0d8e509c932..3e6562ec9d1 100644
--- a/gcc/ipa-ref.h
+++ b/gcc/ipa-ref.h
@@ -59,6 +59,7 @@ public:
   symtab_node *referred;
   gimple *stmt;
   unsigned int lto_stmt_uid;
+  unsigned int speculative_id;
   unsigned int referred_index;
   ENUM_BITFIELD (ipa_ref_use) use:3;
   unsigned int speculative:1;
diff --git a/gcc/ipa.c b/gcc/ipa.c
index 0c92980db46..b5d3769b6fd 100644
--- a/gcc/ipa.c
+++ b/gcc/ipa.c
@@ -166,7 +166,7 @@ process_references (symtab_node *snode,
    devirtualization happens.  After inlining still keep their declarations
    around, so we can devirtualize to a direct call.
 
-   Also try to make trivial devirutalization when no or only one target is
+   Also try to make trivial devirtualization when no or only one target is
    possible.  */
 
 static void
diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
index a4a70e7848c..ef66f128ff4 100644
--- a/gcc/lto-cgraph.c
+++ b/gcc/lto-cgraph.c
@@ -235,6 +235,7 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
   unsigned int uid;
   intptr_t ref;
   struct bitpack_d bp;
+  unsigned len;
 
   if (edge->indirect_unknown_callee)
     streamer_write_enum (ob->main_stream, LTO_symtab_tags, LTO_symtab_last_tag,
@@ -262,6 +263,7 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
   bp_pack_enum (&bp, cgraph_inline_failed_t,
 	        CIF_N_REASONS, edge->inline_failed);
   bp_pack_var_len_unsigned (&bp, uid);
+  bp_pack_var_len_unsigned (&bp, edge->speculative_id);
   bp_pack_value (&bp, edge->indirect_inlining_edge, 1);
   bp_pack_value (&bp, edge->speculative, 1);
   bp_pack_value (&bp, edge->call_stmt_cannot_inline_p, 1);
@@ -288,11 +290,27 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
   streamer_write_bitpack (&bp);
   if (edge->indirect_unknown_callee)
     {
-      streamer_write_hwi_stream (ob->main_stream,
-			         edge->indirect_info->common_target_id);
-      if (edge->indirect_info->common_target_id)
-	streamer_write_hwi_stream
-	   (ob->main_stream, edge->indirect_info->common_target_probability);
+      indirect_target_info *item;
+      unsigned int i;
+      len = edge->has_indirect_call_p ()
+	      ? edge->indirect_info->indirect_call_targets->length ()
+	      : 0;
+      gcc_assert (len <= GCOV_TOPN_VALUES);
+
+      streamer_write_hwi_stream (ob->main_stream, len);
+
+      if (len)
+	{
+	  FOR_EACH_VEC_SAFE_ELT (edge->indirect_info->indirect_call_targets, i,
+				 item)
+	    {
+	      gcc_assert (item->common_target_id);
+	      streamer_write_hwi_stream (ob->main_stream,
+					 item->common_target_id);
+	      streamer_write_hwi_stream (ob->main_stream,
+					 item->common_target_probability);
+	    }
+	}
     }
 }
 
@@ -685,6 +703,7 @@ lto_output_ref (struct lto_simple_output_block *ob, struct ipa_ref *ref,
       if (ref->stmt)
 	uid = gimple_uid (ref->stmt) + 1;
       streamer_write_hwi_stream (ob->main_stream, uid);
+      streamer_write_hwi_stream (ob->main_stream, ref->speculative_id);
     }
 }
 
@@ -1416,7 +1435,10 @@ input_ref (class lto_input_block *ib,
   ref = referring_node->create_reference (node, use);
   ref->speculative = speculative;
   if (is_a <cgraph_node *> (referring_node))
-    ref->lto_stmt_uid = streamer_read_hwi (ib);
+    {
+      ref->lto_stmt_uid = streamer_read_hwi (ib);
+      ref->speculative_id = streamer_read_hwi (ib);
+    }
 }
 
 /* Read an edge from IB.  NODES points to a vector of previously read nodes for
@@ -1430,11 +1452,12 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
 {
   struct cgraph_node *caller, *callee;
   struct cgraph_edge *edge;
-  unsigned int stmt_id;
+  unsigned int stmt_id, speculative_id;
   profile_count count;
   cgraph_inline_failed_t inline_failed;
   struct bitpack_d bp;
   int ecf_flags = 0;
+  unsigned i, len;
 
   caller = dyn_cast<cgraph_node *> (nodes[streamer_read_hwi (ib)]);
   if (caller == NULL || caller->decl == NULL_TREE)
@@ -1454,6 +1477,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
   bp = streamer_read_bitpack (ib);
   inline_failed = bp_unpack_enum (&bp, cgraph_inline_failed_t, CIF_N_REASONS);
   stmt_id = bp_unpack_var_len_unsigned (&bp);
+  speculative_id = bp_unpack_var_len_unsigned (&bp);
 
   if (indirect)
     edge = caller->create_indirect_edge (NULL, 0, count);
@@ -1463,6 +1487,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
   edge->indirect_inlining_edge = bp_unpack_value (&bp, 1);
   edge->speculative = bp_unpack_value (&bp, 1);
   edge->lto_stmt_uid = stmt_id;
+  edge->speculative_id = speculative_id;
   edge->inline_failed = inline_failed;
   edge->call_stmt_cannot_inline_p = bp_unpack_value (&bp, 1);
   edge->can_throw_external = bp_unpack_value (&bp, 1);
@@ -1482,9 +1507,20 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
       if (bp_unpack_value (&bp, 1))
 	ecf_flags |= ECF_RETURNS_TWICE;
       edge->indirect_info->ecf_flags = ecf_flags;
-      edge->indirect_info->common_target_id = streamer_read_hwi (ib);
-      if (edge->indirect_info->common_target_id)
-        edge->indirect_info->common_target_probability = streamer_read_hwi (ib);
+
+      len = streamer_read_hwi (ib);
+
+      gcc_assert (len <= GCOV_TOPN_VALUES);
+
+      if (len)
+	{
+	  for (i = 0; i < len; i++)
+	    {
+	      indirect_target_info item (streamer_read_hwi (ib),
+					 streamer_read_hwi (ib));
+	      vec_safe_push (edge->indirect_info->indirect_call_targets, item);
+	    }
+	}
     }
 }
 
diff --git a/gcc/predict.c b/gcc/predict.c
index 8c66a27d8b6..73be9e7c357 100644
--- a/gcc/predict.c
+++ b/gcc/predict.c
@@ -761,7 +761,6 @@ dump_prediction (FILE *file, enum br_predictor predictor, int probability,
       && bb->count.precise_p ()
       && reason == REASON_NONE)
     {
-      gcc_assert (e->count ().precise_p ());
       fprintf (file, ";;heuristics;%s;%" PRId64 ";%" PRId64 ";%.1f;\n",
 	       predictor_info[predictor].name,
 	       bb->count.to_gcov_type (), e->count ().to_gcov_type (),
diff --git a/gcc/symtab.c b/gcc/symtab.c
index 3e634e22c86..0254524dabc 100644
--- a/gcc/symtab.c
+++ b/gcc/symtab.c
@@ -603,6 +603,7 @@ symtab_node::create_reference (symtab_node *referred_node,
   ref->referred = referred_node;
   ref->stmt = stmt;
   ref->lto_stmt_uid = 0;
+  ref->speculative_id = 0;
   ref->use = use_type;
   ref->speculative = 0;
 
@@ -660,6 +661,7 @@ symtab_node::clone_references (symtab_node *node)
       ref2 = create_reference (ref->referred, ref->use, ref->stmt);
       ref2->speculative = speculative;
       ref2->lto_stmt_uid = stmt_uid;
+      ref2->speculative_id = ref->speculative_id;
     }
 }
 
@@ -678,6 +680,7 @@ symtab_node::clone_referring (symtab_node *node)
       ref2 = ref->referring->create_reference (this, ref->use, ref->stmt);
       ref2->speculative = speculative;
       ref2->lto_stmt_uid = stmt_uid;
+      ref2->speculative_id = ref->speculative_id;
     }
 }
 
@@ -693,6 +696,7 @@ symtab_node::clone_reference (ipa_ref *ref, gimple *stmt)
   ref2 = create_reference (ref->referred, ref->use, stmt);
   ref2->speculative = speculative;
   ref2->lto_stmt_uid = stmt_uid;
+  ref2->speculative_id = ref->speculative_id;
   return ref2;
 }
 
@@ -747,6 +751,7 @@ symtab_node::clear_stmts_in_references (void)
       {
 	r->stmt = NULL;
 	r->lto_stmt_uid = 0;
+	r->speculative_id = 0;
       }
 }
 
diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
new file mode 100644
index 00000000000..a13b08cd60e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
@@ -0,0 +1,33 @@
+/* { dg-require-effective-target lto } */
+/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate" } */
+
+#include <stdio.h>
+
+typedef int (*fptr) (int);
+int
+one (int a);
+
+int
+two (int a);
+
+fptr table[] = {&one, &two};
+
+int
+main()
+{
+  int i, x;
+  fptr p = &one;
+
+  x = one (3);
+
+  for (i = 0; i < 350000000; i++)
+    {
+      x = (*p) (3);
+      p = table[x];
+    }
+  printf ("done:%d\n", x);
+}
+
+/* { dg-final-use-not-autofdo { scan-pgo-wpa-ipa-dump "2 \\(200.00%\\) speculations produced." "profile_estimate" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
new file mode 100644
index 00000000000..a8c6e365fb9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
@@ -0,0 +1,22 @@
+/* It seems there is no way to avoid the other source of mulitple
+   source testcase from being compiled independently.  Just avoid
+   error.  */
+#ifdef DOJOB
+int
+one (int a)
+{
+  return 1;
+}
+
+int
+two (int a)
+{
+  return 0;
+}
+#else
+int
+main()
+{
+  return 0;
+}
+#endif
diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
new file mode 100644
index 00000000000..9b996fcf0ed
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
@@ -0,0 +1,40 @@
+/* { dg-require-effective-target lto } */
+/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate" } */
+
+#include <stdio.h>
+
+typedef int (*fptr) (int);
+int
+one (int a);
+
+int
+two (int a);
+
+fptr table[] = {&one, &two};
+
+int foo ()
+{
+  int i, x;
+  fptr p = &one;
+
+  x = one (3);
+
+  for (i = 0; i < 350000000; i++)
+    {
+      x = (*p) (3);
+      p = table[x];
+    }
+  return x;
+}
+
+int
+main()
+{
+  int x = foo ();
+  printf ("done:%d\n", x);
+}
+
+/* { dg-final-use-not-autofdo { scan-pgo-wpa-ipa-dump "2 \\(200.00%\\) speculations produced." "profile_estimate" } } */
+
diff --git a/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
new file mode 100644
index 00000000000..063996c71df
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
@@ -0,0 +1,37 @@
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-options "-O2 -fdump-ipa-profile_estimate" } */
+
+#include <stdio.h>
+
+typedef int (*fptr) (int);
+int
+one (int a)
+{
+  return 1;
+}
+
+int
+two (int a)
+{
+  return 0;
+}
+
+fptr table[] = {&one, &two};
+
+int
+main()
+{
+  int i, x;
+  fptr p = &one;
+
+  one (3);
+
+  for (i = 0; i < 350000000; i++)
+    {
+      x = (*p) (3);
+      p = table[x];
+    }
+  printf ("done:%d\n", x);
+}
+
+/* { dg-final-use-not-autofdo { scan-ipa-dump "2 \\(200.00%\\) speculations produced." "profile_estimate" } } */
diff --git a/gcc/testsuite/lib/scandump.exp b/gcc/testsuite/lib/scandump.exp
index 42f5c01aa60..8b7cd7cfc16 100644
--- a/gcc/testsuite/lib/scandump.exp
+++ b/gcc/testsuite/lib/scandump.exp
@@ -70,6 +70,7 @@ proc scan-dump { args } {
     set output_file "[glob -nocomplain $dumpbase.[lindex $args 2]]"
     if { $output_file == "" } {
 	verbose -log "$testcase: dump file does not exist"
+	verbose -log "dump file: $dumpbase.$suf"
 	unresolved "$testname"
 	return
     }
diff --git a/gcc/testsuite/lib/scanwpaipa.exp b/gcc/testsuite/lib/scanwpaipa.exp
index b5549fd688e..8aafd6c82e8 100644
--- a/gcc/testsuite/lib/scanwpaipa.exp
+++ b/gcc/testsuite/lib/scanwpaipa.exp
@@ -45,6 +45,29 @@ proc scan-wpa-ipa-dump { args } {
     }
 }
 
+# Argument 0 is the regexp to match
+# Argument 1 is the name of the dumped ipa pass
+# Argument 2 handles expected failures and the like
+proc scan-pgo-wpa-ipa-dump { args } {
+
+    if { [llength $args] < 2 } {
+	error "scan-pgo-wpa-ipa-dump: too few arguments"
+	return
+    }
+    if { [llength $args] > 3 } {
+	error "scan-pgo-wpa-ipa-dump: too many arguments"
+	return
+    }
+    if { [llength $args] >= 3 } {
+	scan-dump "pgo-wpa-ipa" [lindex $args 0] \
+		  "\[0-9\]\[0-9\]\[0-9\]i.[lindex $args 1]" ".x02.wpa" \
+		  [lindex $args 2]
+    } else {
+	scan-dump "pgo-wpa-ipa" [lindex $args 0] \
+		  "\[0-9\]\[0-9\]\[0-9\]i.[lindex $args 1]" ".x02.wpa"
+    }
+}
+
 # Call pass if pattern is present given number of times, otherwise fail.
 # Argument 0 is the regexp to match
 # Argument 1 is number of times the regexp must be found
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index bdc332dcc23..6c8ebffa11d 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -2194,6 +2194,25 @@ copy_bb (copy_body_data *id, basic_block bb,
 
 			  gcc_assert (!edge->indirect_unknown_callee);
 			  old_edge->speculative_call_info (direct, indirect, ref);
+			  while (old_edge->next_callee
+				 && old_edge->next_callee->speculative
+				 && indirect->has_multiple_indirect_call_p ())
+			    {
+			      /* Some speculative calls may contain more than
+				 one direct target, loop iterate it to clone all
+				 related direct edges before cloning the related
+				 indirect edge.  */
+			      id->dst_node->clone_reference (ref, stmt);
+
+			      edge = old_edge->next_callee;
+			      edge = edge->clone (id->dst_node, call_stmt,
+						  gimple_uid (stmt), num, den,
+						  true);
+			      old_edge = old_edge->next_callee;
+			      gcc_assert (!edge->indirect_unknown_callee);
+			      old_edge->speculative_call_info (direct, indirect,
+							       ref);
+			    }
 
 			  profile_count indir_cnt = indirect->count;
 			  indirect = indirect->clone (id->dst_node, call_stmt,
diff --git a/gcc/tree-profile.c b/gcc/tree-profile.c
index 6a4e62f5bae..b4435b9b2a8 100644
--- a/gcc/tree-profile.c
+++ b/gcc/tree-profile.c
@@ -73,8 +73,8 @@ static GTY(()) tree ic_tuple_callee_field;
 /* Do initialization work for the edge profiler.  */
 
 /* Add code:
-   __thread gcov*	__gcov_indirect_call_counters; // pointer to actual counter
-   __thread void*	__gcov_indirect_call_callee; // actual callee address
+   __thread gcov*	__gcov_indirect_call.counters; // pointer to actual counter
+   __thread void*	__gcov_indirect_call.callee; // actual callee address
    __thread int __gcov_function_counter; // time profiler function counter
 */
 static void
@@ -381,7 +381,7 @@ gimple_gen_ic_profiler (histogram_value value, unsigned tag)
       f_1 = foo;
       __gcov_indirect_call.counters = &__gcov4.main[0];
       PROF_9 = f_1;
-      __gcov_indirect_call_callee = PROF_9;
+      __gcov_indirect_call.callee = PROF_9;
       _4 = f_1 ();
    */
 
@@ -444,11 +444,11 @@ gimple_gen_ic_func_profiler (void)
 
   /* Insert code:
 
-     if (__gcov_indirect_call_callee != NULL)
+     if (__gcov_indirect_call.callee != NULL)
        __gcov_indirect_call_profiler_v3 (profile_id, &current_function_decl);
 
      The function __gcov_indirect_call_profiler_v3 is responsible for
-     resetting __gcov_indirect_call_callee to NULL.  */
+     resetting __gcov_indirect_call.callee to NULL.  */
 
   gimple_stmt_iterator gsi = gsi_start_bb (cond_bb);
   void0 = build_int_cst (ptr_type_node, 0);
@@ -890,7 +890,7 @@ pass_ipa_tree_profile::gate (function *)
 {
   /* When profile instrumentation, use or test coverage shall be performed.
      But for AutoFDO, this there is no instrumentation, thus this pass is
-     diabled.  */
+     disabled.  */
   return (!in_lto_p && !flag_auto_profile
 	  && (flag_branch_probabilities || flag_test_coverage
 	      || profile_arc_flag));
diff --git a/gcc/value-prof.c b/gcc/value-prof.c
index cc3542f0295..f64f515c1ee 100644
--- a/gcc/value-prof.c
+++ b/gcc/value-prof.c
@@ -106,7 +106,7 @@ static bool gimple_divmod_fixed_value_transform (gimple_stmt_iterator *);
 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 gimple_ic_transform (gimple_stmt_iterator *);
 
 /* Allocate histogram value.  */
 
@@ -616,8 +616,7 @@ gimple_value_profile_transformations (void)
 	  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;
@@ -628,6 +627,9 @@ gimple_value_profile_transformations (void)
 		  gsi = gsi_for_stmt (stmt);
 		}
 	    }
+
+	  /* The function never thansforms a GIMPLE statement.  */
+	  gimple_ic_transform (&gsi);
         }
     }
 
@@ -1386,13 +1388,12 @@ gimple_ic (gcall *icall_stmt, struct cgraph_node *direct_call,
   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:
- */
+/* There maybe multiple indirect targets in histogram.  Check every
+   indirect/virtual call if callee function exists, if not exist, leave it to
+   LTO stage for later process.  Modify code of this indirect call to an if-else
+   structure in ipa-profile finally.  */
 
-static bool
+static void
 gimple_ic_transform (gimple_stmt_iterator *gsi)
 {
   gcall *stmt;
@@ -1402,52 +1403,58 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
 
   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;
+    return;
 
-  if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
-				  &count, &all))
-    return false;
+  count = 0;
+  all = histogram->hvalue.counters[0];
 
-  if (4 * count <= 3 * all)
-    return false;
+  for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
+    {
+      if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
+				      &count, &all, j))
+	return;
 
-  direct_call = find_func_by_profile_id ((int)val);
+      /* Minimum probability.  should be higher than 25%.  */
+      if (4 * count <= all)
+	return;
 
-  if (direct_call == NULL)
-    {
-      if (val)
+      direct_call = find_func_by_profile_id ((int) val);
+
+      if (direct_call == NULL)
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
-			     "Indirect call -> direct call from other "
-			     "module %T=> %i (will resolve only with LTO)\n",
-			     gimple_call_fn (stmt), (int)val);
+	  if (val)
+	    {
+	      if (dump_enabled_p ())
+		dump_printf_loc (
+		  MSG_MISSED_OPTIMIZATION, stmt,
+		  "Indirect call -> direct call from other "
+		  "module %T=> %i (will resolve only with LTO)\n",
+		  gimple_call_fn (stmt), (int) val);
+	    }
+	  return;
 	}
-      return false;
-    }
 
-  if (dump_enabled_p ())
-    {
-      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
-		       "Indirect call -> direct call "
-		       "%T => %T transformation on insn postponed\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_enabled_p ())
+	{
+	  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
+			   "Indirect call -> direct call "
+			   "%T => %T transformation on insn postponed\n",
+			   gimple_call_fn (stmt), direct_call->decl);
+	  dump_printf_loc (MSG_NOTE, stmt,
+			   "hist->count %" PRId64 " hist->all %" PRId64 "\n",
+			   count, all);
+	}
     }
-
-  return true;
 }
 
 /* Return true if the stringop CALL shall be profiled.  SIZE_ARG be
diff --git a/gcc/value-prof.h b/gcc/value-prof.h
index 77c06f60096..b3eeb57d37d 100644
--- a/gcc/value-prof.h
+++ b/gcc/value-prof.h
@@ -89,7 +89,6 @@ void verify_histograms (void);
 void free_histograms (function *);
 void stringop_block_profile (gimple *, unsigned int *, HOST_WIDE_INT *);
 gcall *gimple_ic (gcall *, struct cgraph_node *, profile_probability);
-bool check_ic_target (gcall *, struct cgraph_node *);
 bool get_nth_most_common_value (gimple *stmt, const char *counter_type,
 				histogram_value hist, gcov_type *value,
 				gcov_type *count, gcov_type *all,
-- 
2.21.0.777.g83232e3864


^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: Ping*2: [PATCH v5] Missed function specialization + partial devirtualization
  2019-11-14  6:01                     ` Ping*2: " luoxhu
@ 2019-11-14  9:15                       ` Jan Hubicka
  2019-11-16 10:14                         ` luoxhu
  0 siblings, 1 reply; 38+ messages in thread
From: Jan Hubicka @ 2019-11-14  9:15 UTC (permalink / raw)
  To: luoxhu; +Cc: Martin Liška, gcc-patches, segher, wschmidt

> 	PR ipa/69678
> 	* cgraph.c (symbol_table::create_edge): Init speculative_id.
> 	(cgraph_edge::make_speculative): Add param for setting speculative_id.
> 	(cgraph_edge::speculative_call_info): Find reference by
> 	speculative_id for multiple indirect targets.
> 	(cgraph_edge::resolve_speculation): Decrease the speculations
> 	for indirect edge, drop it's speculative if not direct target
> 	left.
> 	(cgraph_edge::redirect_call_stmt_to_callee): Likewise.
> 	(cgraph_node::verify_node): Don't report error if speculative
> 	edge not include statement.
> 	(cgraph_edge::has_multiple_indirect_call_p): New function.
> 	(cgraph_edge::has_indirect_call_p): New function.
> 	* cgraph.h (struct indirect_target_info): New struct.
> 	(indirect_call_targets): New vector variable.
> 	(make_speculative): Add param for setting speculative_id.
> 	(cgraph_edge::has_multiple_indirect_call_p): New declare.
> 	(cgraph_edge::has_indirect_call_p): New declare.
> 	(speculative_id): New variable.
> 	* cgraphclones.c (cgraph_node::create_clone): Clone speculative_id.
> 	* cgraphunit.c: Fix comments typo.
> 	* ipa-comdats.c: Fix comments typo.
> 	* ipa-inline.c (inline_small_functions): Fix iterator update.
> 	* ipa-profile.c (ipa_profile_generate_summary): Add indirect
> 	multiple targets logic.
> 	(ipa_profile): Likewise.
> 	* ipa-ref.h (speculative_id): New variable.
> 	* ipa.c (process_references): Fix typo.
> 	* lto-cgraph.c (lto_output_edge): Add indirect multiple targets
> 	logic.  Stream out speculative_id.
> 	(input_edge): Likewise.
> 	* predict.c (dump_prediction): Revome edges count assert to be
> 	precise.
> 	* symtab.c (symtab_node::create_reference): Init speculative_id.
> 	(symtab_node::clone_references): Clone speculative_id.
> 	(symtab_node::clone_referring): Clone speculative_id.
> 	(symtab_node::clone_reference): Clone speculative_id.
> 	(symtab_node::clear_stmts_in_references): Clear speculative_id.
> 	* tree-inline.c (copy_bb): Duplicate all the speculative edges
> 	if indirect call contains multiple speculative targets.
> 	* tree-profile.c (gimple_gen_ic_profiler): Use the new variable
> 	__gcov_indirect_call.counters and __gcov_indirect_call.callee.
> 	(gimple_gen_ic_func_profiler): Likewise.
> 	(pass_ipa_tree_profile::gate): Fix comment typos.
> 	* value-prof.h  (check_ic_target): Remove.
> 	* value-prof.c  (gimple_value_profile_transformations):
> 	Use void function gimple_ic_transform.
> 	* value-prof.c  (gimple_ic_transform): Handle topn case.
> 	Fix comment typos.  Change it to a void function.
> 
> gcc/testsuite/ChangeLog
> 
> 	2019-11-14  Xiong Hu Luo  <luoxhu@linux.ibm.com>
> 
> 	PR ipa/69678
> 	* gcc.dg/tree-prof/indir-call-prof-topn.c: New testcase.
> 	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c: New testcase.
> 	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c: New testcase.
> 	* gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c: New testcase.
> 	* lib/scandump.exp: Dump executable file name.
> 	* lib/scanwpaipa.exp: New scan-pgo-wap-ipa-dump.
> @@ -1089,6 +1093,38 @@ cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
>     call) and if one of them exists, all of them must exist.
>  
>     Given speculative call edge, return all three components.
> +
> +   For some indirect edge, it may maps to multiple direct edges, i.e.  1:N.
> +   check the speculative_id to return all the three components for specified
> +   direct edge or indirect edge.
> +   If input is indirect, caller of this function will get the direct edge one by
> +   one, get_edge will just return one of the direct edge mapped to the indirect
> +   edge, the returned direct edge will be resolved or redirected by the caller,
> +   then number of indirect calls (speculations) is deceased in each access.
> +   If input is direct, this function will get the indirect edge and reference
> +   with matched speculative_id, the returned edge will also be resolved or
> +   redirected, decrease the speculations accordingly.
> +   Speculations of indirect edge will be dropped only if all direct edges
> +   be handled.
> +
> +   e.g.  for indirect edge E statement "call call_dest":
> +
> +   Redirect N3 after redirected N2:
> +
> +   if (call_dest == N2)
> +     n2 ();
> +   else if (call_dest == N3)
> +     n3 ();
> +   else
> +     call call_dest
> +
> +   Resolve N3 and only redirect N2:
> +
> +   if (call_dest == N2)
> +     n2 ();
> +   else
> +     call call_dest
> +

I find this comment hard to read. Reader probably does not know what
speculative edges are and we only want to describe speculative_call_info
function not also the way we resolve calls.  So what about something
like this:

  Speculative calls represent a transformation of indirect calls
  which may be later inserted into gimple in the following form:

   if (call_dest == target1)
     target1 ();
   else if (call_dest == target2)
     target ();
   else
     call_dest ();

  This is a win in case target1 and target2 are common values for
  call_dest as determined by ipa-devirt or indirect call profiling.
  In particular this may enable inlining and other optimizations.

  Speculative call consists of the following main components:

  1) One or more direct call
  2) One or more IPA_REF_ADDR references (representing the fact that
     code above takes address of target1 and target2)
  3) The fallback indirect call

  Direct calls and corresponidng references are linked by
  speculative_id.

  speculative_call_info returns tripple
    (direct_call, IPA_REF_ADDR reference, indirect call)
  when called on one edge participating in the speculative call.

  If called on direct call its corresponding IPA_REF_ADDR is returned.

  If caled on indirect call it will return one of direct edges and its
  matching IPA_REF_ADDR.

  All direct calls corresponding to a given speculative call can be
  enumerated by indirect_edge->indirect_info->indirect_call_targets
>   */
>  
>  void
> @@ -1128,7 +1164,7 @@ cgraph_edge::speculative_call_info (cgraph_edge *&direct,
>  
>    reference = NULL;
>    for (i = 0; e->caller->iterate_reference (i, ref); i++)
> -    if (ref->speculative
> +    if (ref->speculative && ref->speculative_id == e->speculative_id
>  	&& ((ref->stmt && ref->stmt == e->call_stmt)
>  	    || (!ref->stmt && ref->lto_stmt_uid == e->lto_stmt_uid)))
>        {
> @@ -1189,7 +1225,21 @@ cgraph_edge::resolve_speculation (tree callee_decl)

Rewrite comment here to make clear what resolve_speculation does for
multiple targets.
>           in the functions inlined through it.  */
>      }
>    edge->count += e2->count;
> -  edge->speculative = false;
> +  /* edge is indirect, e2 is direct.  If edge contains multiple speculations,
> +     remove one of speculations for this indirect edge, then if edge still
> +     contains direct target, keep the speculation, next direct target
> +     will continue use it.  Give up speculation completely if no direct
> +     target is left for this indirect edge.  */
> +  if (edge->has_indirect_call_p ())
> +    {
> +      /* As the direct targets are sorted by decrease, delete the first target
> +	 when it is resolved.  */
> +      edge->indirect_info->indirect_call_targets->ordered_remove (0);
> +      if (edge->indirect_info->indirect_call_targets->is_empty ())
> +	edge->speculative = false;
> +    }
> +  else
> +    edge->speculative = false;
>    e2->speculative = false;
>    ref->remove_reference ();
>    if (e2->indirect_unknown_callee || e2->inline_failed)
> @@ -1297,7 +1347,21 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
>  	  e->caller->set_call_stmt_including_clones (e->call_stmt, new_stmt,
>  						     false);
>  	  e->count = gimple_bb (e->call_stmt)->count;
> -	  e2->speculative = false;
> +	  /* edge is direct, e2 is indirect here.  If e2 contains multiple
> +	     speculations, remove one of speculations for this indirect edge,
> +	     then if e2 still contains direct target, keep the speculation,
> +	     next direct target will continue use it.  Give up speculation
> +	     completely if no direct target is left for this indirect e2.  */
> +	  if (e2->has_indirect_call_p ())
> +	    {
> +	      /* As the direct targets are sorted by decrease, delete the first
> +		 target when it is redirected.  */
> +	      e2->indirect_info->indirect_call_targets->ordered_remove (0);
> +	      if (e2->indirect_info->indirect_call_targets->is_empty ())
> +		e2->speculative = false;
> +	    }
> +	  else
> +	    e2->speculative = false;

I think this also needs better explanation (and especially in the
comment in the front of function)
> @@ -3696,6 +3760,22 @@ cgraph_edge::possibly_call_in_translation_unit_p (void)
>    return node->get_availability () >= AVAIL_INTERPOSABLE;
>  }
>  
> +/* Return true if this edge has multiple indirect call targets.  */
> +bool
> +cgraph_edge::has_multiple_indirect_call_p (void)
Probaby better as 
multiple_speculative_call_targets_p

It seems to me that here you use indirect_call_targets array which does
not make much sense once the indirect edge is turned into speculative
call.  This vector should be freed after that and as I write late
it should not be in the callgraph itself but rather in a ipa-profile
summary.

(I know that common_target_probability and common_target_id is there
right now but it is code predating summaries and it does not belong there
anymore)

I would simply add "num_speculative_call_targets" into
cgraph_indirect_call_info datastructure.
> +{
> +  return (indirect_info && indirect_info->indirect_call_targets
> +	  && indirect_info->indirect_call_targets->length () > 1);

Maybe it would make sense to call this speculative_call_targets?
Since they are after all direct calls...
> +}
> +
> +/* Return true if this edge has at least one indirect call target.  */
> +bool
> +cgraph_edge::has_indirect_call_p (void)
> +{
> +  return (indirect_info && indirect_info->indirect_call_targets
> +	  && !indirect_info->indirect_call_targets->is_empty ());

How this is different from speculative flag on an indirect call?
> +}
> +
>  /* A stashed copy of "symtab" for use by selftest::symbol_table_test.
>     This needs to be a global so that it can be a GC root, and thus
>     prevent the stashed copy from being garbage-collected if the GC runs
> diff --git a/gcc/cgraph.h b/gcc/cgraph.h
> index a4f14743f00..1299702788d 100644
> --- a/gcc/cgraph.h
> +++ b/gcc/cgraph.h
> @@ -1636,6 +1636,21 @@ private:
>    void make_speculative (tree otr_type = NULL);
>  };
>  
> +/* Structure containing indirect target information from profile.  */
> +
> +struct GTY (()) indirect_target_info
Probably better as speculative_call_target?
> +{
> +  indirect_target_info (unsigned int id, int prob)
> +    : common_target_id (id), common_target_probability (prob)
> +  {
> +  }
> +
> +  /* Profile_id of common target obtained from profile.  */
> +  unsigned int common_target_id;
> +  /* Probability that call will land in function with COMMON_TARGET_ID.  */
> +  int common_target_probability;

I think this would be more like frequent then common, but perhaps
target_id and target_probablity is better.
> +};
> +
>  /* Structure containing additional information about an indirect call.  */
>  
>  class GTY(()) cgraph_indirect_call_info
> @@ -1654,10 +1669,9 @@ public:
>    int param_index;
>    /* ECF flags determined from the caller.  */
>    int ecf_flags;
> -  /* Profile_id of common target obtained from profile.  */
> -  int common_target_id;
> -  /* Probability that call will land in function with COMMON_TARGET_ID.  */
> -  int common_target_probability;
> +
> +  /* An indirect call may contain one or multiple call targets.  */
> +  vec<indirect_target_info, va_gc> *indirect_call_targets;

And speculative_call_targets?
Please put these into cgraph_edge_summary template.

This is a short lived annotation which is private to ipa-profile and
does not need to be in the core datastructures. So you can then also
delcare it within ipa-profile.c as no one lese cares about it.
Also stream in ipa_profile_write_summary rather than lto-cgraph.

> +  /* speculative id is used by multiple indirect targets when the function is
... is used to link direct calls with their corresponding IPA_REF_ADDR
references when representing speculative cals.  */

Even if there is only one speculation the values must match, right? :)
> +   speculated.  */
> +  unsigned int speculative_id;

The bitfields will pack well with the speculative_id if we make it
16bit rather than 32bit. I do not think we will ever want to support
more than 64K speculative targets.
> diff --git a/gcc/ipa-comdats.c b/gcc/ipa-comdats.c
> index b496497ff66..0672d6d6575 100644
> --- a/gcc/ipa-comdats.c
> +++ b/gcc/ipa-comdats.c
> @@ -18,7 +18,7 @@ along with GCC; see the file COPYING3.  If not see
>  <http://www.gnu.org/licenses/>.  */
>  
>  /* This is very simple pass that looks for static symbols that are used
> -   exlusively by symbol within one comdat group.  In this case it makes
> +   exclusively by symbol within one comdat group.  In this case it makes
This can go in separately as obovius.
>     sense to bring the symbol itself into the group to avoid dead code
>     that would arrise when the comdat group from current unit is replaced
>     by a different copy.  Consider for example:
> diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
> index 78ec0ec685f..8fc67ef88a5 100644
> --- a/gcc/ipa-inline.c
> +++ b/gcc/ipa-inline.c
> @@ -1945,12 +1945,15 @@ inline_small_functions (void)
>  	}
>        if (has_speculative)
>  	for (edge = node->callees; edge; edge = next)
> -	  if (edge->speculative && !speculation_useful_p (edge,
> -							  edge->aux != NULL))
> -	    {
> -	      edge->resolve_speculation ();
> -	      update = true;
> -	    }
> +	  {
> +	    next = edge->next_callee;
> +	    if (edge->speculative
> +		&& !speculation_useful_p (edge, edge->aux != NULL))
> +	      {
> +		edge->resolve_speculation ();
> +		update = true;
> +	      }
> +	  }

This looks like pasto.  So next is initialized only in the loop before
and it happens that it never has speculative flag on it?

If so, this is also independent fix, so please send it as separate
patch.
> @@ -563,7 +573,7 @@ ipa_profile (void)
>    histogram.release ();
>    histogram_pool.release ();
>  
> -  /* Produce speculative calls: we saved common traget from porfiling into
> +  /* Produce speculative calls: we saved common target from profiling into

Also just commit this as obvious.
> diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
> index bdc332dcc23..6c8ebffa11d 100644
> --- a/gcc/tree-inline.c
> +++ b/gcc/tree-inline.c
> @@ -2194,6 +2194,25 @@ copy_bb (copy_body_data *id, basic_block bb,
>  
>  			  gcc_assert (!edge->indirect_unknown_callee);
>  			  old_edge->speculative_call_info (direct, indirect, ref);
> +			  while (old_edge->next_callee
> +				 && old_edge->next_callee->speculative
> +				 && indirect->has_multiple_indirect_call_p ())
> +			    {
> +			      /* Some speculative calls may contain more than
> +				 one direct target, loop iterate it to clone all
I gues better as "Iterate through all direct calls associated to the
speculative call and clone all..."
> +				 related direct edges before cloning the related
> +				 indirect edge.  */
> +			      id->dst_node->clone_reference (ref, stmt);
> +
> +			      edge = old_edge->next_callee;
> +			      edge = edge->clone (id->dst_node, call_stmt,
> +						  gimple_uid (stmt), num, den,
> +						  true);
> +			      old_edge = old_edge->next_callee;
> +			      gcc_assert (!edge->indirect_unknown_callee);
> +			      old_edge->speculative_call_info (direct, indirect,
> +							       ref);
Why do you call this at the end of loop? And how do you know that al the
edges are associated to the given speculative call?
> +			    }
>  
>  			  profile_count indir_cnt = indirect->count;
>  			  indirect = indirect->clone (id->dst_node, call_stmt,
> diff --git a/gcc/tree-profile.c b/gcc/tree-profile.c
> index 6a4e62f5bae..b4435b9b2a8 100644
> --- a/gcc/tree-profile.c
> +++ b/gcc/tree-profile.c
> @@ -73,8 +73,8 @@ static GTY(()) tree ic_tuple_callee_field;
>  /* Do initialization work for the edge profiler.  */
>  
>  /* Add code:
> -   __thread gcov*	__gcov_indirect_call_counters; // pointer to actual counter
> -   __thread void*	__gcov_indirect_call_callee; // actual callee address
> +   __thread gcov*	__gcov_indirect_call.counters; // pointer to actual counter
> +   __thread void*	__gcov_indirect_call.callee; // actual callee address
>     __thread int __gcov_function_counter; // time profiler function counter
>  */
>  static void
> @@ -381,7 +381,7 @@ gimple_gen_ic_profiler (histogram_value value, unsigned tag)
>        f_1 = foo;
>        __gcov_indirect_call.counters = &__gcov4.main[0];
>        PROF_9 = f_1;
> -      __gcov_indirect_call_callee = PROF_9;
> +      __gcov_indirect_call.callee = PROF_9;
>        _4 = f_1 ();
>     */
>  
> @@ -444,11 +444,11 @@ gimple_gen_ic_func_profiler (void)
>  
>    /* Insert code:
>  
> -     if (__gcov_indirect_call_callee != NULL)
> +     if (__gcov_indirect_call.callee != NULL)
>         __gcov_indirect_call_profiler_v3 (profile_id, &current_function_decl);
>  
>       The function __gcov_indirect_call_profiler_v3 is responsible for
> -     resetting __gcov_indirect_call_callee to NULL.  */
> +     resetting __gcov_indirect_call.callee to NULL.  */
>  
>    gimple_stmt_iterator gsi = gsi_start_bb (cond_bb);
>    void0 = build_int_cst (ptr_type_node, 0);
> @@ -890,7 +890,7 @@ pass_ipa_tree_profile::gate (function *)
>  {
>    /* When profile instrumentation, use or test coverage shall be performed.
>       But for AutoFDO, this there is no instrumentation, thus this pass is
> -     diabled.  */
> +     disabled.  */
>    return (!in_lto_p && !flag_auto_profile
>  	  && (flag_branch_probabilities || flag_test_coverage
>  	      || profile_arc_flag));

Also commit this independently?

Patch looks really nice overall. It will be easy to teach ipa-devirt to
use multiple targets too.

I apologize for very late response and thanks for all the pings
(I will try to get more timely)
Honza
> diff --git a/gcc/value-prof.c b/gcc/value-prof.c
> index cc3542f0295..f64f515c1ee 100644
> --- a/gcc/value-prof.c
> +++ b/gcc/value-prof.c
> @@ -106,7 +106,7 @@ static bool gimple_divmod_fixed_value_transform (gimple_stmt_iterator *);
>  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 gimple_ic_transform (gimple_stmt_iterator *);
>  
>  /* Allocate histogram value.  */
>  
> @@ -616,8 +616,7 @@ gimple_value_profile_transformations (void)
>  	  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;
> @@ -628,6 +627,9 @@ gimple_value_profile_transformations (void)
>  		  gsi = gsi_for_stmt (stmt);
>  		}
>  	    }
> +
> +	  /* The function never thansforms a GIMPLE statement.  */
> +	  gimple_ic_transform (&gsi);
>          }
>      }
>  
> @@ -1386,13 +1388,12 @@ gimple_ic (gcall *icall_stmt, struct cgraph_node *direct_call,
>    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:
> - */
> +/* There maybe multiple indirect targets in histogram.  Check every
> +   indirect/virtual call if callee function exists, if not exist, leave it to
> +   LTO stage for later process.  Modify code of this indirect call to an if-else
> +   structure in ipa-profile finally.  */
>  
> -static bool
> +static void
>  gimple_ic_transform (gimple_stmt_iterator *gsi)
>  {
>    gcall *stmt;
> @@ -1402,52 +1403,58 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
>  
>    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;
> +    return;
>  
> -  if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
> -				  &count, &all))
> -    return false;
> +  count = 0;
> +  all = histogram->hvalue.counters[0];
>  
> -  if (4 * count <= 3 * all)
> -    return false;
> +  for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
> +    {
> +      if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
> +				      &count, &all, j))
> +	return;
>  
> -  direct_call = find_func_by_profile_id ((int)val);
> +      /* Minimum probability.  should be higher than 25%.  */
> +      if (4 * count <= all)
> +	return;
>  
> -  if (direct_call == NULL)
> -    {
> -      if (val)
> +      direct_call = find_func_by_profile_id ((int) val);
> +
> +      if (direct_call == NULL)
>  	{
> -	  if (dump_enabled_p ())
> -	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
> -			     "Indirect call -> direct call from other "
> -			     "module %T=> %i (will resolve only with LTO)\n",
> -			     gimple_call_fn (stmt), (int)val);
> +	  if (val)
> +	    {
> +	      if (dump_enabled_p ())
> +		dump_printf_loc (
> +		  MSG_MISSED_OPTIMIZATION, stmt,
> +		  "Indirect call -> direct call from other "
> +		  "module %T=> %i (will resolve only with LTO)\n",
> +		  gimple_call_fn (stmt), (int) val);
> +	    }
> +	  return;
>  	}
> -      return false;
> -    }
>  
> -  if (dump_enabled_p ())
> -    {
> -      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
> -		       "Indirect call -> direct call "
> -		       "%T => %T transformation on insn postponed\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_enabled_p ())
> +	{
> +	  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
> +			   "Indirect call -> direct call "
> +			   "%T => %T transformation on insn postponed\n",
> +			   gimple_call_fn (stmt), direct_call->decl);
> +	  dump_printf_loc (MSG_NOTE, stmt,
> +			   "hist->count %" PRId64 " hist->all %" PRId64 "\n",
> +			   count, all);
> +	}
>      }
> -
> -  return true;
>  }
>  
>  /* Return true if the stringop CALL shall be profiled.  SIZE_ARG be
> diff --git a/gcc/value-prof.h b/gcc/value-prof.h
> index 77c06f60096..b3eeb57d37d 100644
> --- a/gcc/value-prof.h
> +++ b/gcc/value-prof.h
> @@ -89,7 +89,6 @@ void verify_histograms (void);
>  void free_histograms (function *);
>  void stringop_block_profile (gimple *, unsigned int *, HOST_WIDE_INT *);
>  gcall *gimple_ic (gcall *, struct cgraph_node *, profile_probability);
> -bool check_ic_target (gcall *, struct cgraph_node *);
>  bool get_nth_most_common_value (gimple *stmt, const char *counter_type,
>  				histogram_value hist, gcov_type *value,
>  				gcov_type *count, gcov_type *all,
> -- 
> 2.21.0.777.g83232e3864
> 
> 

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: Ping*2: [PATCH v5] Missed function specialization + partial devirtualization
  2019-11-14  9:15                       ` Jan Hubicka
@ 2019-11-16 10:14                         ` luoxhu
  2019-11-18 13:04                           ` Martin Liška
  0 siblings, 1 reply; 38+ messages in thread
From: luoxhu @ 2019-11-16 10:14 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Martin Liška, gcc-patches, segher, wschmidt

Hi Thanks,

On 2019/11/14 17:04, Jan Hubicka wrote:
>> 	PR ipa/69678
>> 	* cgraph.c (symbol_table::create_edge): Init speculative_id.
>> 	(cgraph_edge::make_speculative): Add param for setting speculative_id.
>> 	(cgraph_edge::speculative_call_info): Find reference by
>> 	speculative_id for multiple indirect targets.
>> 	(cgraph_edge::resolve_speculation): Decrease the speculations
>> 	for indirect edge, drop it's speculative if not direct target
>> 	left.
>> 	(cgraph_edge::redirect_call_stmt_to_callee): Likewise.
>> 	(cgraph_node::verify_node): Don't report error if speculative
>> 	edge not include statement.
>> 	(cgraph_edge::has_multiple_indirect_call_p): New function.
>> 	(cgraph_edge::has_indirect_call_p): New function.
>> 	* cgraph.h (struct indirect_target_info): New struct.
>> 	(indirect_call_targets): New vector variable.
>> 	(make_speculative): Add param for setting speculative_id.
>> 	(cgraph_edge::has_multiple_indirect_call_p): New declare.
>> 	(cgraph_edge::has_indirect_call_p): New declare.
>> 	(speculative_id): New variable.
>> 	* cgraphclones.c (cgraph_node::create_clone): Clone speculative_id.
>> 	* cgraphunit.c: Fix comments typo.
>> 	* ipa-comdats.c: Fix comments typo.
>> 	* ipa-inline.c (inline_small_functions): Fix iterator update.
>> 	* ipa-profile.c (ipa_profile_generate_summary): Add indirect
>> 	multiple targets logic.
>> 	(ipa_profile): Likewise.
>> 	* ipa-ref.h (speculative_id): New variable.
>> 	* ipa.c (process_references): Fix typo.
>> 	* lto-cgraph.c (lto_output_edge): Add indirect multiple targets
>> 	logic.  Stream out speculative_id.
>> 	(input_edge): Likewise.
>> 	* predict.c (dump_prediction): Revome edges count assert to be
>> 	precise.
>> 	* symtab.c (symtab_node::create_reference): Init speculative_id.
>> 	(symtab_node::clone_references): Clone speculative_id.
>> 	(symtab_node::clone_referring): Clone speculative_id.
>> 	(symtab_node::clone_reference): Clone speculative_id.
>> 	(symtab_node::clear_stmts_in_references): Clear speculative_id.
>> 	* tree-inline.c (copy_bb): Duplicate all the speculative edges
>> 	if indirect call contains multiple speculative targets.
>> 	* tree-profile.c (gimple_gen_ic_profiler): Use the new variable
>> 	__gcov_indirect_call.counters and __gcov_indirect_call.callee.
>> 	(gimple_gen_ic_func_profiler): Likewise.
>> 	(pass_ipa_tree_profile::gate): Fix comment typos.
>> 	* value-prof.h  (check_ic_target): Remove.
>> 	* value-prof.c  (gimple_value_profile_transformations):
>> 	Use void function gimple_ic_transform.
>> 	* value-prof.c  (gimple_ic_transform): Handle topn case.
>> 	Fix comment typos.  Change it to a void function.
>>
>> gcc/testsuite/ChangeLog
>>
>> 	2019-11-14  Xiong Hu Luo  <luoxhu@linux.ibm.com>
>>
>> 	PR ipa/69678
>> 	* gcc.dg/tree-prof/indir-call-prof-topn.c: New testcase.
>> 	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c: New testcase.
>> 	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c: New testcase.
>> 	* gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c: New testcase.
>> 	* lib/scandump.exp: Dump executable file name.
>> 	* lib/scanwpaipa.exp: New scan-pgo-wap-ipa-dump.
>> @@ -1089,6 +1093,38 @@ cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
>>      call) and if one of them exists, all of them must exist.
>>   
>>      Given speculative call edge, return all three components.
>> +
>> +   For some indirect edge, it may maps to multiple direct edges, i.e.  1:N.
>> +   check the speculative_id to return all the three components for specified
>> +   direct edge or indirect edge.
>> +   If input is indirect, caller of this function will get the direct edge one by
>> +   one, get_edge will just return one of the direct edge mapped to the indirect
>> +   edge, the returned direct edge will be resolved or redirected by the caller,
>> +   then number of indirect calls (speculations) is deceased in each access.
>> +   If input is direct, this function will get the indirect edge and reference
>> +   with matched speculative_id, the returned edge will also be resolved or
>> +   redirected, decrease the speculations accordingly.
>> +   Speculations of indirect edge will be dropped only if all direct edges
>> +   be handled.
>> +
>> +   e.g.  for indirect edge E statement "call call_dest":
>> +
>> +   Redirect N3 after redirected N2:
>> +
>> +   if (call_dest == N2)
>> +     n2 ();
>> +   else if (call_dest == N3)
>> +     n3 ();
>> +   else
>> +     call call_dest
>> +
>> +   Resolve N3 and only redirect N2:
>> +
>> +   if (call_dest == N2)
>> +     n2 ();
>> +   else
>> +     call call_dest
>> +
> 
> I find this comment hard to read. Reader probably does not know what
> speculative edges are and we only want to describe speculative_call_info
> function not also the way we resolve calls.  So what about something
> like this:
> 
>    Speculative calls represent a transformation of indirect calls
>    which may be later inserted into gimple in the following form:
> 
>     if (call_dest == target1)
>       target1 ();
>     else if (call_dest == target2)
>       target ();
>     else
>       call_dest ();
> 
>    This is a win in case target1 and target2 are common values for
>    call_dest as determined by ipa-devirt or indirect call profiling.
>    In particular this may enable inlining and other optimizations.
> 
>    Speculative call consists of the following main components:
> 
>    1) One or more direct call
>    2) One or more IPA_REF_ADDR references (representing the fact that
>       code above takes address of target1 and target2)
>    3) The fallback indirect call
> 
>    Direct calls and corresponidng references are linked by
>    speculative_id.
> 
>    speculative_call_info returns tripple
>      (direct_call, IPA_REF_ADDR reference, indirect call)
>    when called on one edge participating in the speculative call.
> 
>    If called on direct call its corresponding IPA_REF_ADDR is returned.
> 
>    If caled on indirect call it will return one of direct edges and its
>    matching IPA_REF_ADDR.
> 
>    All direct calls corresponding to a given speculative call can be
>    enumerated by indirect_edge->indirect_info->indirect_call_targets
Will update.

>>    */
>>   
>>   void
>> @@ -1128,7 +1164,7 @@ cgraph_edge::speculative_call_info (cgraph_edge *&direct,
>>   
>>     reference = NULL;
>>     for (i = 0; e->caller->iterate_reference (i, ref); i++)
>> -    if (ref->speculative
>> +    if (ref->speculative && ref->speculative_id == e->speculative_id
>>   	&& ((ref->stmt && ref->stmt == e->call_stmt)
>>   	    || (!ref->stmt && ref->lto_stmt_uid == e->lto_stmt_uid)))
>>         {
>> @@ -1189,7 +1225,21 @@ cgraph_edge::resolve_speculation (tree callee_decl)
> 
> Rewrite comment here to make clear what resolve_speculation does for
> multiple targets.
OK and please be patient with my English :) 

>>            in the functions inlined through it.  */
>>       }
>>     edge->count += e2->count;
>> -  edge->speculative = false;
>> +  /* edge is indirect, e2 is direct.  If edge contains multiple speculations,
>> +     remove one of speculations for this indirect edge, then if edge still
>> +     contains direct target, keep the speculation, next direct target
>> +     will continue use it.  Give up speculation completely if no direct
>> +     target is left for this indirect edge.  */
>> +  if (edge->has_indirect_call_p ())
>> +    {
>> +      /* As the direct targets are sorted by decrease, delete the first target
>> +	 when it is resolved.  */
>> +      edge->indirect_info->indirect_call_targets->ordered_remove (0);
>> +      if (edge->indirect_info->indirect_call_targets->is_empty ())
>> +	edge->speculative = false;
>> +    }
>> +  else
>> +    edge->speculative = false;
>>     e2->speculative = false;
>>     ref->remove_reference ();
>>     if (e2->indirect_unknown_callee || e2->inline_failed)
>> @@ -1297,7 +1347,21 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
>>   	  e->caller->set_call_stmt_including_clones (e->call_stmt, new_stmt,
>>   						     false);
>>   	  e->count = gimple_bb (e->call_stmt)->count;
>> -	  e2->speculative = false;
>> +	  /* edge is direct, e2 is indirect here.  If e2 contains multiple
>> +	     speculations, remove one of speculations for this indirect edge,
>> +	     then if e2 still contains direct target, keep the speculation,
>> +	     next direct target will continue use it.  Give up speculation
>> +	     completely if no direct target is left for this indirect e2.  */
>> +	  if (e2->has_indirect_call_p ())
>> +	    {
>> +	      /* As the direct targets are sorted by decrease, delete the first
>> +		 target when it is redirected.  */
>> +	      e2->indirect_info->indirect_call_targets->ordered_remove (0);
>> +	      if (e2->indirect_info->indirect_call_targets->is_empty ())
>> +		e2->speculative = false;
>> +	    }
>> +	  else
>> +	    e2->speculative = false;
> 
> I think this also needs better explanation (and especially in the
> comment in the front of function)
>> @@ -3696,6 +3760,22 @@ cgraph_edge::possibly_call_in_translation_unit_p (void)
>>     return node->get_availability () >= AVAIL_INTERPOSABLE;
>>   }
>>   
>> +/* Return true if this edge has multiple indirect call targets.  */
>> +bool
>> +cgraph_edge::has_multiple_indirect_call_p (void)
> Probaby better as
> multiple_speculative_call_targets_p
> 
> It seems to me that here you use indirect_call_targets array which does
> not make much sense once the indirect edge is turned into speculative
> call.  This vector should be freed after that and as I write late
> it should not be in the callgraph itself but rather in a ipa-profile
> summary.
> 
> (I know that common_target_probability and common_target_id is there
> right now but it is code predating summaries and it does not belong there
> anymore)
> 
> I would simply add "num_speculative_call_targets" into
> cgraph_indirect_call_info datastructure.

OK.  I used num_of_ics(i.e. num_speculative_call_targets) in early version.
Will try restore it.

>> +{
>> +  return (indirect_info && indirect_info->indirect_call_targets
>> +	  && indirect_info->indirect_call_targets->length () > 1);
> 
> Maybe it would make sense to call this speculative_call_targets?
> Since they are after all direct calls...
>> +}
>> +
>> +/* Return true if this edge has at least one indirect call target.  */
>> +bool
>> +cgraph_edge::has_indirect_call_p (void)
>> +{
>> +  return (indirect_info && indirect_info->indirect_call_targets
>> +	  && !indirect_info->indirect_call_targets->is_empty ());
> 
> How this is different from speculative flag on an indirect call?
>> +}
>> +
>>   /* A stashed copy of "symtab" for use by selftest::symbol_table_test.
>>      This needs to be a global so that it can be a GC root, and thus
>>      prevent the stashed copy from being garbage-collected if the GC runs
>> diff --git a/gcc/cgraph.h b/gcc/cgraph.h
>> index a4f14743f00..1299702788d 100644
>> --- a/gcc/cgraph.h
>> +++ b/gcc/cgraph.h
>> @@ -1636,6 +1636,21 @@ private:
>>     void make_speculative (tree otr_type = NULL);
>>   };
>>   
>> +/* Structure containing indirect target information from profile.  */
>> +
>> +struct GTY (()) indirect_target_info
> Probably better as speculative_call_target?
>> +{
>> +  indirect_target_info (unsigned int id, int prob)
>> +    : common_target_id (id), common_target_probability (prob)
>> +  {
>> +  }
>> +
>> +  /* Profile_id of common target obtained from profile.  */
>> +  unsigned int common_target_id;
>> +  /* Probability that call will land in function with COMMON_TARGET_ID.  */
>> +  int common_target_probability;
> 
> I think this would be more like frequent then common, but perhaps
> target_id and target_probablity is better.
>> +};
>> +
>>   /* Structure containing additional information about an indirect call.  */
>>   
>>   class GTY(()) cgraph_indirect_call_info
>> @@ -1654,10 +1669,9 @@ public:
>>     int param_index;
>>     /* ECF flags determined from the caller.  */
>>     int ecf_flags;
>> -  /* Profile_id of common target obtained from profile.  */
>> -  int common_target_id;
>> -  /* Probability that call will land in function with COMMON_TARGET_ID.  */
>> -  int common_target_probability;
>> +
>> +  /* An indirect call may contain one or multiple call targets.  */
>> +  vec<indirect_target_info, va_gc> *indirect_call_targets;
> 
> And speculative_call_targets?
> Please put these into cgraph_edge_summary template.
> 
> This is a short lived annotation which is private to ipa-profile and
> does not need to be in the core datastructures. So you can then also
> delcare it within ipa-profile.c as no one lese cares about it.
> Also stream in ipa_profile_write_summary rather than lto-cgraph.

Sorry that I don't quite understand your meanning here.  I didn't grep the
word "cgraph_edge_summary" in source code, do you mean add new structure
and related functions like "static ipa_sra_call_summaries *call_sums;"
in ipa-sra.c and stream in/out in ipa_profile_write_summary/ipa_profile_read_summary?

BTW, there is already an ipa_call_summaries in ipa-profile.c:355,  feel apologetic as 
not familiar with many summaries in it, will take time to go through it:
ipa_call_summary *s = ipa_call_summaries->get (edge);

> 
>> +  /* speculative id is used by multiple indirect targets when the function is
> ... is used to link direct calls with their corresponding IPA_REF_ADDR
> references when representing speculative cals.  */
> 
> Even if there is only one speculation the values must match, right? :)
>> +   speculated.  */
>> +  unsigned int speculative_id;
> 
> The bitfields will pack well with the speculative_id if we make it
> 16bit rather than 32bit. I do not think we will ever want to support
> more than 64K speculative targets.
OK.

>> diff --git a/gcc/ipa-comdats.c b/gcc/ipa-comdats.c
>> index b496497ff66..0672d6d6575 100644
>> --- a/gcc/ipa-comdats.c
>> +++ b/gcc/ipa-comdats.c
>> @@ -18,7 +18,7 @@ along with GCC; see the file COPYING3.  If not see
>>   <http://www.gnu.org/licenses/>.  */
>>   
>>   /* This is very simple pass that looks for static symbols that are used
>> -   exlusively by symbol within one comdat group.  In this case it makes
>> +   exclusively by symbol within one comdat group.  In this case it makes
> This can go in separately as obovius.
>>      sense to bring the symbol itself into the group to avoid dead code
>>      that would arrise when the comdat group from current unit is replaced
>>      by a different copy.  Consider for example:
>> diff --git a/gcc/ipa-inline.c b/gcc/ipa-inline.c
>> index 78ec0ec685f..8fc67ef88a5 100644
>> --- a/gcc/ipa-inline.c
>> +++ b/gcc/ipa-inline.c
>> @@ -1945,12 +1945,15 @@ inline_small_functions (void)
>>   	}
>>         if (has_speculative)
>>   	for (edge = node->callees; edge; edge = next)
>> -	  if (edge->speculative && !speculation_useful_p (edge,
>> -							  edge->aux != NULL))
>> -	    {
>> -	      edge->resolve_speculation ();
>> -	      update = true;
>> -	    }
>> +	  {
>> +	    next = edge->next_callee;
>> +	    if (edge->speculative
>> +		&& !speculation_useful_p (edge, edge->aux != NULL))
>> +	      {
>> +		edge->resolve_speculation ();
>> +		update = true;
>> +	      }
>> +	  }
> 
> This looks like pasto.  So next is initialized only in the loop before
> and it happens that it never has speculative flag on it?> 
> If so, this is also independent fix, so please send it as separate
> patch.
>> @@ -563,7 +573,7 @@ ipa_profile (void)
>>     histogram.release ();
>>     histogram_pool.release ();
>>   
>> -  /* Produce speculative calls: we saved common traget from porfiling into
>> +  /* Produce speculative calls: we saved common target from profiling into
> 
> Also just commit this as obvious.
Done.

>> diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
>> index bdc332dcc23..6c8ebffa11d 100644
>> --- a/gcc/tree-inline.c
>> +++ b/gcc/tree-inline.c
>> @@ -2194,6 +2194,25 @@ copy_bb (copy_body_data *id, basic_block bb,
>>   
>>   			  gcc_assert (!edge->indirect_unknown_callee);
>>   			  old_edge->speculative_call_info (direct, indirect, ref);
>> +			  while (old_edge->next_callee
>> +				 && old_edge->next_callee->speculative
>> +				 && indirect->has_multiple_indirect_call_p ())
>> +			    {
>> +			      /* Some speculative calls may contain more than
>> +				 one direct target, loop iterate it to clone all
> I gues better as "Iterate through all direct calls associated to the
> speculative call and clone all..."
>> +				 related direct edges before cloning the related
>> +				 indirect edge.  */
>> +			      id->dst_node->clone_reference (ref, stmt);
>> +
>> +			      edge = old_edge->next_callee;
>> +			      edge = edge->clone (id->dst_node, call_stmt,
>> +						  gimple_uid (stmt), num, den,
>> +						  true);
>> +			      old_edge = old_edge->next_callee;
>> +			      gcc_assert (!edge->indirect_unknown_callee);
>> +			      old_edge->speculative_call_info (direct, indirect,
>> +							       ref);
> Why do you call this at the end of loop? And how do you know that al the
> edges are associated to the given speculative call?
Speculative targets are sorted by decrease order of execution count in tree-profile. 
speculative_id in each direct target will ensure the identity in speculative_call_info.

Here, speculative_call_info is called first before the while loop, if direct target1 exists,
it will be cloned to dst_node.  Then speculative_call_info is called second time, if direct
target2 exsits, also clone it to dst_node, else the while condition is false, the indirect
edge is cloned to dst_node in following code out of the while loop.

We all know that you are very busy:), it's so nice of you take the time to review patch to
improve code quality.  Thanks!


Xiong Hu
BR

>> +			    }
>>   
>>   			  profile_count indir_cnt = indirect->count;
>>   			  indirect = indirect->clone (id->dst_node, call_stmt,
>> diff --git a/gcc/tree-profile.c b/gcc/tree-profile.c
>> index 6a4e62f5bae..b4435b9b2a8 100644
>> --- a/gcc/tree-profile.c
>> +++ b/gcc/tree-profile.c
>> @@ -73,8 +73,8 @@ static GTY(()) tree ic_tuple_callee_field;
>>   /* Do initialization work for the edge profiler.  */
>>   
>>   /* Add code:
>> -   __thread gcov*	__gcov_indirect_call_counters; // pointer to actual counter
>> -   __thread void*	__gcov_indirect_call_callee; // actual callee address
>> +   __thread gcov*	__gcov_indirect_call.counters; // pointer to actual counter
>> +   __thread void*	__gcov_indirect_call.callee; // actual callee address
>>      __thread int __gcov_function_counter; // time profiler function counter
>>   */
>>   static void
>> @@ -381,7 +381,7 @@ gimple_gen_ic_profiler (histogram_value value, unsigned tag)
>>         f_1 = foo;
>>         __gcov_indirect_call.counters = &__gcov4.main[0];
>>         PROF_9 = f_1;
>> -      __gcov_indirect_call_callee = PROF_9;
>> +      __gcov_indirect_call.callee = PROF_9;
>>         _4 = f_1 ();
>>      */
>>   
>> @@ -444,11 +444,11 @@ gimple_gen_ic_func_profiler (void)
>>   
>>     /* Insert code:
>>   
>> -     if (__gcov_indirect_call_callee != NULL)
>> +     if (__gcov_indirect_call.callee != NULL)
>>          __gcov_indirect_call_profiler_v3 (profile_id, &current_function_decl);
>>   
>>        The function __gcov_indirect_call_profiler_v3 is responsible for
>> -     resetting __gcov_indirect_call_callee to NULL.  */
>> +     resetting __gcov_indirect_call.callee to NULL.  */
>>   
>>     gimple_stmt_iterator gsi = gsi_start_bb (cond_bb);
>>     void0 = build_int_cst (ptr_type_node, 0);
>> @@ -890,7 +890,7 @@ pass_ipa_tree_profile::gate (function *)
>>   {
>>     /* When profile instrumentation, use or test coverage shall be performed.
>>        But for AutoFDO, this there is no instrumentation, thus this pass is
>> -     diabled.  */
>> +     disabled.  */
>>     return (!in_lto_p && !flag_auto_profile
>>   	  && (flag_branch_probabilities || flag_test_coverage
>>   	      || profile_arc_flag));
> 
> Also commit this independently?
> 
> Patch looks really nice overall. It will be easy to teach ipa-devirt to
> use multiple targets too.
> 
> I apologize for very late response and thanks for all the pings
> (I will try to get more timely)
> Honza
>> diff --git a/gcc/value-prof.c b/gcc/value-prof.c
>> index cc3542f0295..f64f515c1ee 100644
>> --- a/gcc/value-prof.c
>> +++ b/gcc/value-prof.c
>> @@ -106,7 +106,7 @@ static bool gimple_divmod_fixed_value_transform (gimple_stmt_iterator *);
>>   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 gimple_ic_transform (gimple_stmt_iterator *);
>>   
>>   /* Allocate histogram value.  */
>>   
>> @@ -616,8 +616,7 @@ gimple_value_profile_transformations (void)
>>   	  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;
>> @@ -628,6 +627,9 @@ gimple_value_profile_transformations (void)
>>   		  gsi = gsi_for_stmt (stmt);
>>   		}
>>   	    }
>> +
>> +	  /* The function never thansforms a GIMPLE statement.  */
>> +	  gimple_ic_transform (&gsi);
>>           }
>>       }
>>   
>> @@ -1386,13 +1388,12 @@ gimple_ic (gcall *icall_stmt, struct cgraph_node *direct_call,
>>     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:
>> - */
>> +/* There maybe multiple indirect targets in histogram.  Check every
>> +   indirect/virtual call if callee function exists, if not exist, leave it to
>> +   LTO stage for later process.  Modify code of this indirect call to an if-else
>> +   structure in ipa-profile finally.  */
>>   
>> -static bool
>> +static void
>>   gimple_ic_transform (gimple_stmt_iterator *gsi)
>>   {
>>     gcall *stmt;
>> @@ -1402,52 +1403,58 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
>>   
>>     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;
>> +    return;
>>   
>> -  if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
>> -				  &count, &all))
>> -    return false;
>> +  count = 0;
>> +  all = histogram->hvalue.counters[0];
>>   
>> -  if (4 * count <= 3 * all)
>> -    return false;
>> +  for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
>> +    {
>> +      if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
>> +				      &count, &all, j))
>> +	return;
>>   
>> -  direct_call = find_func_by_profile_id ((int)val);
>> +      /* Minimum probability.  should be higher than 25%.  */
>> +      if (4 * count <= all)
>> +	return;
>>   
>> -  if (direct_call == NULL)
>> -    {
>> -      if (val)
>> +      direct_call = find_func_by_profile_id ((int) val);
>> +
>> +      if (direct_call == NULL)
>>   	{
>> -	  if (dump_enabled_p ())
>> -	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
>> -			     "Indirect call -> direct call from other "
>> -			     "module %T=> %i (will resolve only with LTO)\n",
>> -			     gimple_call_fn (stmt), (int)val);
>> +	  if (val)
>> +	    {
>> +	      if (dump_enabled_p ())
>> +		dump_printf_loc (
>> +		  MSG_MISSED_OPTIMIZATION, stmt,
>> +		  "Indirect call -> direct call from other "
>> +		  "module %T=> %i (will resolve only with LTO)\n",
>> +		  gimple_call_fn (stmt), (int) val);
>> +	    }
>> +	  return;
>>   	}
>> -      return false;
>> -    }
>>   
>> -  if (dump_enabled_p ())
>> -    {
>> -      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
>> -		       "Indirect call -> direct call "
>> -		       "%T => %T transformation on insn postponed\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_enabled_p ())
>> +	{
>> +	  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
>> +			   "Indirect call -> direct call "
>> +			   "%T => %T transformation on insn postponed\n",
>> +			   gimple_call_fn (stmt), direct_call->decl);
>> +	  dump_printf_loc (MSG_NOTE, stmt,
>> +			   "hist->count %" PRId64 " hist->all %" PRId64 "\n",
>> +			   count, all);
>> +	}
>>       }
>> -
>> -  return true;
>>   }
>>   
>>   /* Return true if the stringop CALL shall be profiled.  SIZE_ARG be
>> diff --git a/gcc/value-prof.h b/gcc/value-prof.h
>> index 77c06f60096..b3eeb57d37d 100644
>> --- a/gcc/value-prof.h
>> +++ b/gcc/value-prof.h
>> @@ -89,7 +89,6 @@ void verify_histograms (void);
>>   void free_histograms (function *);
>>   void stringop_block_profile (gimple *, unsigned int *, HOST_WIDE_INT *);
>>   gcall *gimple_ic (gcall *, struct cgraph_node *, profile_probability);
>> -bool check_ic_target (gcall *, struct cgraph_node *);
>>   bool get_nth_most_common_value (gimple *stmt, const char *counter_type,
>>   				histogram_value hist, gcov_type *value,
>>   				gcov_type *count, gcov_type *all,
>> -- 
>> 2.21.0.777.g83232e3864
>>
>>

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: Ping*2: [PATCH v5] Missed function specialization + partial devirtualization
  2019-11-16 10:14                         ` luoxhu
@ 2019-11-18 13:04                           ` Martin Liška
  2019-12-03  2:31                             ` [PATCH v6] " luoxhu
  0 siblings, 1 reply; 38+ messages in thread
From: Martin Liška @ 2019-11-18 13:04 UTC (permalink / raw)
  To: luoxhu, Jan Hubicka; +Cc: gcc-patches, segher, wschmidt

On 11/16/19 10:59 AM, luoxhu wrote:
> Sorry that I don't quite understand your meanning here.  I didn't grep the
> word "cgraph_edge_summary" in source code, do you mean add new structure

Hello.

He wanted to write call_summary class and so you need something similar to
ipa-sra.c:431. It's a data structure which associate a data to cgraph_edge.
Is it understandable please?

Martin

> and related functions like "static ipa_sra_call_summaries *call_sums;"
> in ipa-sra.c and stream in/out in ipa_profile_write_summary/ipa_profile_read_summary?
> 
> BTW, there is already an ipa_call_summaries in ipa-profile.c:355,  feel apologetic as
> not familiar with many summaries in it, will take time to go through it:
> ipa_call_summary *s = ipa_call_summaries->get (edge);

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH v6] Missed function specialization + partial devirtualization
  2019-11-18 13:04                           ` Martin Liška
@ 2019-12-03  2:31                             ` luoxhu
  2019-12-18  2:33                               ` *Ping* " luoxhu
  0 siblings, 1 reply; 38+ messages in thread
From: luoxhu @ 2019-12-03  2:31 UTC (permalink / raw)
  To: Martin Liška, Jan Hubicka; +Cc: gcc-patches, segher, wschmidt

Hi Martin and Honza,


On 2019/11/18 21:02, Martin Liška wrote:
> On 11/16/19 10:59 AM, luoxhu wrote:
>> Sorry that I don't quite understand your meanning here.  I didn't grep the
>> word "cgraph_edge_summary" in source code, do you mean add new structure
> 
> Hello.
> 
> He wanted to write call_summary class and so you need something similar to
> ipa-sra.c:431. It's a data structure which associate a data to cgraph_edge.
> Is it understandable please?
> 
> Martin
> 

Update the patch as below with "git format-patch -U15" for review convenience,
the GC issue is fixed after leveraging the full GC framework in ipa-profile.c, Thanks:

v6 Changes:
 1. Define and use speculative_call_targets summary, move
 speculative_call_target from cgraph.h to ipa-profile.c.
 2. Use num_speculative_call_targets in cgraph_indirect_call_info.
 3. Refine with review comments.

This patch aims to fix PR69678 caused by PGO indirect call profiling
performance issues.
The bug that profiling data is never working was fixed by Martin's pull
back of topN patches, performance got GEOMEAN ~1% improvement(+24% for
511.povray_r specifically).
Still, currently the default profile only generates SINGLE indirect target
that called more than 75%.  This patch leverages MULTIPLE indirect
targets use in LTO-WPA and LTO-LTRANS stage, as a result, function
specialization, profiling, partial devirtualization, inlining and
cloning could be done successfully based on it.
Performance can get improved from 0.70 sec to 0.38 sec on simple tests.
Details are:
  1.  PGO with topn is enabled by default now, but only one indirect
  target edge will be generated in ipa-profile pass, so add variables to enable
  multiple speculative edges through passes, speculative_id will record the
  direct edge index bind to the indirect edge, indirect_call_targets length
  records how many direct edges owned by the indirect edge, postpone gimple_ic
  to ipa-profile like default as inline pass will decide whether it is benefit
  to transform indirect call.
  2.  Use speculative_id to track and search the reference node matched
  with the direct edge's callee for multiple targets.  Actually, it is the
  caller's responsibility to handle the direct edges mapped to same indirect
  edge.  speculative_call_info will return one of the direct edge specified,
  this will leverage current IPA edge process framework mostly.
  3.  Enable LTO WPA/LTRANS stage multiple indirect call targets analysis for
  profile full support in ipa passes and cgraph_edge functions.  speculative_id
  can be set by make_speculative id when multiple targets are binded to
  one indirect edge, and cloned if new edge is cloned.  speculative_id
  is streamed out and stream int by lto like lto_stmt_uid.
  4.  Add 1 in module testcase and 2 cross module testcases.
  5.  Bootstrap and regression test passed on Power8-LE.  No function
  and performance regression for SPEC2017.

gcc/ChangeLog

	2019-12-02  Xiong Hu Luo  <luoxhu@linux.ibm.com>

	PR ipa/69678
	* Makefile.in (GTFILES): Add ipa-profile.c.
	* cgraph.c (symbol_table::create_edge): Init speculative_id.
	(cgraph_edge::make_speculative): Add param for setting speculative_id.
	(cgraph_edge::speculative_call_info): Update comments and find reference
	by speculative_id for multiple indirect targets.
	(cgraph_edge::resolve_speculation): Decrease the speculations
	for indirect edge, drop it's speculative if not direct target
	left. Update comments.
	(cgraph_edge::redirect_call_stmt_to_callee): Likewise.
	(cgraph_node::dump): Print num_speculative_call_targets.
	(cgraph_node::verify_node): Don't report error if speculative
	edge not include statement.
	(cgraph_edge::num_speculative_call_targets_p): New function.
	* cgraph.h (int common_target_id): Remove.
	(int common_target_probability): Remove.
	(num_speculative_call_targets): New variable.
	(make_speculative): Add param for setting speculative_id.
	(cgraph_edge::num_speculative_call_targets_p): New declare.
	(speculative_id): New variable.
	* cgraphclones.c (cgraph_node::create_clone): Clone speculative_id.
	* ipa-profile.c (struct speculative_call_target): New struct.
	(class speculative_call_summary): New class.
	(class speculative_call_summaries): New class.
	(call_sums): New variable.
	(ipa_profile_generate_summary): Generate indirect multiple targets summaries.
	(ipa_profile_write_edge_summary): New function.
	(ipa_profile_write_summary): Stream out indirect multiple targets summaries.
	(ipa_profile_dump_all_summaries): New function.
	(ipa_profile_read_edge_summary): New function.
	(ipa_profile_read_summary_section): New function.
	(ipa_profile_read_summary): Stream in indirect multiple targets summaries.
	(ipa_profile): Generate num_speculative_call_targets from
	summaries.
	* ipa-ref.h (speculative_id): New variable.
	* lto-cgraph.c (lto_output_edge): Remove indirect common_target_id and
	common_target_probability.   Stream out speculative_id and
	num_speculative_call_targets.
	(input_edge): Likewise.
	* predict.c (dump_prediction): Remove edges count assert to be
	precise.
	* symtab.c (symtab_node::create_reference): Init speculative_id.
	(symtab_node::clone_references): Clone speculative_id.
	(symtab_node::clone_referring): Clone speculative_id.
	(symtab_node::clone_reference): Clone speculative_id.
	(symtab_node::clear_stmts_in_references): Clear speculative_id.
	* tree-inline.c (copy_bb): Duplicate all the speculative edges
	if indirect call contains multiple speculative targets.
	* tree-profile.c (gimple_gen_ic_profiler): Use the new variable
	__gcov_indirect_call.counters and __gcov_indirect_call.callee.
	(gimple_gen_ic_func_profiler): Likewise.
	(pass_ipa_tree_profile::gate): Fix comment typos.
	* value-prof.h  (check_ic_target): Remove.
	* value-prof.c  (gimple_value_profile_transformations):
	Use void function gimple_ic_transform.
	* value-prof.c  (gimple_ic_transform): Handle topn case.
	Fix comment typos.  Change it to a void function.

gcc/testsuite/ChangeLog

	2019-12-02  Xiong Hu Luo  <luoxhu@linux.ibm.com>

	PR ipa/69678
	* gcc.dg/tree-prof/indir-call-prof-topn.c: New testcase.
	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c: New testcase.
	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c: New testcase.
	* gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c: New testcase.
	* lib/scandump.exp: Dump executable file name.
	* lib/scanwpaipa.exp: New scan-pgo-wap-ipa-dump.
---
 gcc/Makefile.in                               |   1 +
 gcc/cgraph.c                                  | 106 ++++-
 gcc/cgraph.h                                  |  16 +-
 gcc/cgraphclones.c                            |   1 +
 gcc/ipa-profile.c                             | 384 ++++++++++++++++--
 gcc/ipa-ref.h                                 |   1 +
 gcc/lto-cgraph.c                              |  28 +-
 gcc/predict.c                                 |   1 -
 gcc/symtab.c                                  |   5 +
 .../tree-prof/crossmodule-indir-call-topn-1.c |  33 ++
 .../crossmodule-indir-call-topn-1a.c          |  22 +
 .../tree-prof/crossmodule-indir-call-topn-2.c |  40 ++
 .../gcc.dg/tree-prof/indir-call-prof-topn.c   |  37 ++
 gcc/testsuite/lib/scandump.exp                |   1 +
 gcc/testsuite/lib/scanwpaipa.exp              |  23 ++
 gcc/tree-inline.c                             |  22 +
 gcc/value-prof.c                              |  87 ++--
 gcc/value-prof.h                              |   1 -
 18 files changed, 705 insertions(+), 104 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 7d3c13230e4..366aceb8330 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -2578,30 +2578,31 @@ GTFILES = $(CPPLIB_H) $(srcdir)/input.h $(srcdir)/coretypes.h \
   $(srcdir)/omp-offload.c \
   $(srcdir)/omp-expand.c \
   $(srcdir)/omp-low.c \
   $(srcdir)/targhooks.c $(out_file) $(srcdir)/passes.c $(srcdir)/cgraphunit.c \
   $(srcdir)/cgraphclones.c \
   $(srcdir)/tree-phinodes.c \
   $(srcdir)/tree-ssa-alias.h \
   $(srcdir)/tree-ssanames.h \
   $(srcdir)/tree-vrp.h \
   $(srcdir)/ipa-prop.h \
   $(srcdir)/trans-mem.c \
   $(srcdir)/lto-streamer.h \
   $(srcdir)/target-globals.h \
   $(srcdir)/ipa-predicate.h \
   $(srcdir)/ipa-fnsummary.h \
+  $(srcdir)/ipa-profile.c \
   $(srcdir)/value-range.h \
   $(srcdir)/vtable-verify.c \
   $(srcdir)/asan.c \
   $(srcdir)/ubsan.c \
   $(srcdir)/tsan.c \
   $(srcdir)/sanopt.c \
   $(srcdir)/sancov.c \
   $(srcdir)/ipa-devirt.c \
   $(srcdir)/internal-fn.h \
   $(srcdir)/hsa-common.c \
   $(srcdir)/calls.c \
   $(srcdir)/omp-general.h \
   @all_gtfiles@
 
 # Compute the list of GT header files from the corresponding C sources,
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index b75430f3f3a..8aa4cc91939 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -852,30 +852,31 @@ symbol_table::create_edge (cgraph_node *caller, cgraph_node *callee,
 
   edge = ggc_alloc<cgraph_edge> ();
   edge->m_summary_id = -1;
   edges_count++;
 
   gcc_assert (++edges_max_uid != 0);
   edge->m_uid = edges_max_uid;
   edge->aux = NULL;
   edge->caller = caller;
   edge->callee = callee;
   edge->prev_caller = NULL;
   edge->next_caller = NULL;
   edge->prev_callee = NULL;
   edge->next_callee = NULL;
   edge->lto_stmt_uid = 0;
+  edge->speculative_id = 0;
 
   edge->count = count;
   edge->call_stmt = call_stmt;
   edge->indirect_info = NULL;
   edge->indirect_inlining_edge = 0;
   edge->speculative = false;
   edge->indirect_unknown_callee = indir_unknown_callee;
   if (call_stmt && caller->call_site_hash)
     cgraph_add_edge_to_call_site_hash (edge);
 
   if (cloning_p)
     return edge;
 
   edge->can_throw_external
     = call_stmt ? stmt_can_throw_external (DECL_STRUCT_FUNCTION (caller->decl),
@@ -1041,66 +1042,95 @@ cgraph_edge::remove (void)
    At clone materialization time, the indirect call E will
    be expanded as:
 
    if (call_dest == N2)
      n2 ();
    else
      call call_dest
 
    At this time the function just creates the direct call,
    the reference representing the if conditional and attaches
    them all to the original indirect call statement.  
 
    Return direct edge created.  */
 
 cgraph_edge *
-cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
+cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count,
+			       unsigned int speculative_id)
 {
   cgraph_node *n = caller;
   ipa_ref *ref = NULL;
   cgraph_edge *e2;
 
   if (dump_file)
     fprintf (dump_file, "Indirect call -> speculative call %s => %s\n",
 	     n->dump_name (), n2->dump_name ());
   speculative = true;
   e2 = n->create_edge (n2, call_stmt, direct_count);
   initialize_inline_failed (e2);
   e2->speculative = true;
   if (TREE_NOTHROW (n2->decl))
     e2->can_throw_external = false;
   else
     e2->can_throw_external = can_throw_external;
   e2->lto_stmt_uid = lto_stmt_uid;
+  e2->speculative_id = speculative_id;
   e2->in_polymorphic_cdtor = in_polymorphic_cdtor;
   count -= e2->count;
   symtab->call_edge_duplication_hooks (this, e2);
   ref = n->create_reference (n2, IPA_REF_ADDR, call_stmt);
   ref->lto_stmt_uid = lto_stmt_uid;
+  ref->speculative_id = speculative_id;
   ref->speculative = speculative;
   n2->mark_address_taken ();
   return e2;
 }
 
-/* Speculative call consist of three components:
-   1) an indirect edge representing the original call
-   2) an direct edge representing the new call
-   3) ADDR_EXPR reference representing the speculative check.
-   All three components are attached to single statement (the indirect
-   call) and if one of them exists, all of them must exist.
+/* Speculative calls represent a transformation of indirect calls
+   which may be later inserted into gimple in the following form:
 
-   Given speculative call edge, return all three components.
+   if (call_dest == target1)
+   target1 ();
+   else if (call_dest == target2)
+   target2 ();
+   else
+   call_dest ();
+
+   This is a win in case when target1 and target2 are common values for
+   call_dest as determined by ipa-devirt or indirect call profiling.
+   In particular this may enable inlining and other optimizations.
+
+   Speculative call consists of the following main components:
+
+   1) One or more "speculative" direct call (num_speculative_call_targets is
+   speculative direct call count belongs to the speculative indirect call)
+   2) One or more IPA_REF_ADDR references (representing the fact that code above
+   takes address of target1 and target2)
+   3) The fallback "speculative" indirect call
+
+   Direct calls and corresponding references are linked by
+   speculative_id.
+
+   speculative_call_info returns triple
+   (direct_call, indirect call, IPA_REF_ADDR reference)
+   when called on one edge participating in the speculative call:
+
+   1) If called on direct call, its corresponding IPA_REF_ADDR and related
+   indirect call are returned.
+
+   2) If called on indirect call, it will return one of direct edges and its
+   matching IPA_REF_ADDR.
  */
 
 void
 cgraph_edge::speculative_call_info (cgraph_edge *&direct,
 				    cgraph_edge *&indirect,
 				    ipa_ref *&reference)
 {
   ipa_ref *ref;
   int i;
   cgraph_edge *e2;
   cgraph_edge *e = this;
 
   if (!e->indirect_unknown_callee)
     for (e2 = e->caller->indirect_calls;
 	 e2->call_stmt != e->call_stmt || e2->lto_stmt_uid != e->lto_stmt_uid;
@@ -1116,47 +1146,56 @@ cgraph_edge::speculative_call_info (cgraph_edge *&direct,
 	  gcc_assert (e->speculative && !e->indirect_unknown_callee);
 	}
       else
 	for (e = e->caller->callees; 
 	     e2->call_stmt != e->call_stmt
 	     || e2->lto_stmt_uid != e->lto_stmt_uid;
 	     e = e->next_callee)
 	  ;
     }
   gcc_assert (e->speculative && e2->speculative);
   direct = e;
   indirect = e2;
 
   reference = NULL;
   for (i = 0; e->caller->iterate_reference (i, ref); i++)
-    if (ref->speculative
+    if (ref->speculative && ref->speculative_id == e->speculative_id
 	&& ((ref->stmt && ref->stmt == e->call_stmt)
 	    || (!ref->stmt && ref->lto_stmt_uid == e->lto_stmt_uid)))
       {
 	reference = ref;
 	break;
       }
 
   /* Speculative edge always consist of all three components - direct edge,
      indirect and reference.  */
   
   gcc_assert (e && e2 && ref);
 }
 
 /* Speculative call edge turned out to be direct call to CALLEE_DECL.
    Remove the speculative call sequence and return edge representing the call.
-   It is up to caller to redirect the call as appropriate. */
+
+   For "speculative" indirect call that contains multiple "speculative"
+   targets (i.e. edge->indirect_info->num_speculative_call_targets > 1),
+   decrease the count and only remove current direct edge.
+
+   If no speculative direct call left to the speculative indirect call, remove
+   the speculative of both the indirect call and corresponding direct edge.
+
+   It is up to caller to iteratively resolve each "speculative" direct call and
+   redirect the call as appropriate.  */
 
 cgraph_edge *
 cgraph_edge::resolve_speculation (tree callee_decl)
 {
   cgraph_edge *edge = this;
   cgraph_edge *e2;
   ipa_ref *ref;
 
   gcc_assert (edge->speculative);
   edge->speculative_call_info (e2, edge, ref);
   if (!callee_decl
       || !ref->referred->semantically_equivalent_p
 	   (symtab_node::get (callee_decl)))
     {
       if (dump_file)
@@ -1177,31 +1216,40 @@ cgraph_edge::resolve_speculation (tree callee_decl)
 		       e2->callee->dump_name ());
 	    }
 	}
     }
   else
     {
       cgraph_edge *tmp = edge;
       if (dump_file)
         fprintf (dump_file, "Speculative call turned into direct call.\n");
       edge = e2;
       e2 = tmp;
       /* FIXME:  If EDGE is inlined, we should scale up the frequencies and counts
          in the functions inlined through it.  */
     }
   edge->count += e2->count;
-  edge->speculative = false;
+  if (edge->num_speculative_call_targets_p ())
+    {
+      /* The indirect edge has multiple speculative targets, don't remove
+	 speculative until all related direct edges are resolved.  */
+      edge->indirect_info->num_speculative_call_targets--;
+      if (!edge->indirect_info->num_speculative_call_targets)
+	edge->speculative = false;
+    }
+  else
+    edge->speculative = false;
   e2->speculative = false;
   ref->remove_reference ();
   if (e2->indirect_unknown_callee || e2->inline_failed)
     e2->remove ();
   else
     e2->callee->remove_symbol_and_inline_clones ();
   if (edge->caller->call_site_hash)
     cgraph_update_edge_in_call_site_hash (edge);
   return edge;
 }
 
 /* Make an indirect edge with an unknown callee an ordinary edge leading to
    CALLEE.  DELTA is an integer constant that is to be added to the this
    pointer (first parameter) to compensate for skipping a thunk adjustment.  */
 
@@ -1237,31 +1285,41 @@ cgraph_edge::make_direct (cgraph_node *callee)
   prev_callee = NULL;
   next_callee = caller->callees;
   if (caller->callees)
     caller->callees->prev_callee = edge;
   caller->callees = edge;
 
   /* Insert to callers list of the new callee.  */
   edge->set_callee (callee);
 
   /* We need to re-determine the inlining status of the edge.  */
   initialize_inline_failed (edge);
   return edge;
 }
 
 /* If necessary, change the function declaration in the call statement
-   associated with E so that it corresponds to the edge callee.  */
+   associated with E so that it corresponds to the edge callee.
+
+   The edge could be one of speculative direct call generated from speculative
+   indirect call.  In this circumstance, decrease the speculative targets
+   count (i.e. num_speculative_call_targets) and redirect call stmt to the
+   corresponding i-th target.  If no speculative direct call left to the
+   speculative indirect call, remove "speculative" of the indirect call and
+   also redirect stmt to it's final direct target.
+
+   It is up to caller to iteratively transform each "speculative"
+   direct call as appropriate.  */
 
 gimple *
 cgraph_edge::redirect_call_stmt_to_callee (void)
 {
   cgraph_edge *e = this;
 
   tree decl = gimple_call_fndecl (e->call_stmt);
   gcall *new_stmt;
   gimple_stmt_iterator gsi;
 
   if (e->speculative)
     {
       cgraph_edge *e2;
       gcall *new_stmt;
       ipa_ref *ref;
@@ -1285,31 +1343,41 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
 	    }
 	  gcc_assert (e2->speculative);
 	  push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
 
 	  profile_probability prob = e->count.probability_in (e->count
 							      + e2->count);
 	  if (!prob.initialized_p ())
 	    prob = profile_probability::even ();
 	  new_stmt = gimple_ic (e->call_stmt,
 				dyn_cast<cgraph_node *> (ref->referred),
 				prob);
 	  e->speculative = false;
 	  e->caller->set_call_stmt_including_clones (e->call_stmt, new_stmt,
 						     false);
 	  e->count = gimple_bb (e->call_stmt)->count;
-	  e2->speculative = false;
+	  if (e2->num_speculative_call_targets_p ())
+	    {
+	      /* The indirect edge has multiple speculative targets, don't
+		 remove speculative until all related direct edges are
+		 redirected.  */
+	      e2->indirect_info->num_speculative_call_targets--;
+	      if (!e2->indirect_info->num_speculative_call_targets)
+		e2->speculative = false;
+	    }
+	  else
+	    e2->speculative = false;
 	  e2->count = gimple_bb (e2->call_stmt)->count;
 	  ref->speculative = false;
 	  ref->stmt = NULL;
 	  /* Indirect edges are not both in the call site hash.
 	     get it updated.  */
 	  if (e->caller->call_site_hash)
 	    cgraph_update_edge_in_call_site_hash (e2);
 	  pop_cfun ();
 	  /* Continue redirecting E to proper target.  */
 	}
     }
 
 
   if (e->indirect_unknown_callee
       || decl == e->callee->decl)
@@ -2097,30 +2165,32 @@ cgraph_node::dump (FILE *f)
 	}
       else
         fprintf (f, "   Indirect call");
       edge->dump_edge_flags (f);
       if (edge->indirect_info->param_index != -1)
 	{
 	  fprintf (f, " of param:%i", edge->indirect_info->param_index);
 	  if (edge->indirect_info->agg_contents)
 	   fprintf (f, " loaded from %s %s at offset %i",
 		    edge->indirect_info->member_ptr ? "member ptr" : "aggregate",
 		    edge->indirect_info->by_ref ? "passed by reference":"",
 		    (int)edge->indirect_info->offset);
 	  if (edge->indirect_info->vptr_changed)
 	    fprintf (f, " (vptr maybe changed)");
 	}
+      fprintf (f, " Num speculative call targets: %i",
+	       edge->indirect_info->num_speculative_call_targets);
       fprintf (f, "\n");
       if (edge->indirect_info->polymorphic)
 	edge->indirect_info->context.dump (f);
     }
 }
 
 /* Dump call graph node to file F in graphviz format.  */
 
 void
 cgraph_node::dump_graphviz (FILE *f)
 {
   cgraph_edge *edge;
 
   for (edge = callees; edge; edge = edge->next_callee)
     {
@@ -3374,31 +3444,31 @@ cgraph_node::verify_node (void)
 	      }
 	    for (i = 0; iterate_reference (i, ref); i++)
 	      if (ref->stmt && !stmts.contains (ref->stmt))
 		{
 		  error ("reference to dead statement");
 		  cgraph_debug_gimple_stmt (this_cfun, ref->stmt);
 		  error_found = true;
 		}
 	}
       else
 	/* No CFG available?!  */
 	gcc_unreachable ();
 
       for (e = callees; e; e = e->next_callee)
 	{
-	  if (!e->aux)
+	  if (!e->aux && !e->speculative)
 	    {
 	      error ("edge %s->%s has no corresponding call_stmt",
 		     identifier_to_locale (e->caller->name ()),
 		     identifier_to_locale (e->callee->name ()));
 	      cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
 	      error_found = true;
 	    }
 	  e->aux = 0;
 	}
       for (e = indirect_calls; e; e = e->next_callee)
 	{
 	  if (!e->aux && !e->speculative)
 	    {
 	      error ("an indirect edge from %s has no corresponding call_stmt",
 		     identifier_to_locale (e->caller->name ()));
@@ -3711,30 +3781,38 @@ cgraph_edge::possibly_call_in_translation_unit_p (void)
   if (!TREE_PUBLIC (callee->decl) && !DECL_EXTERNAL (callee->decl))
     return true;
 
   /* Otherwise we need to lookup prevailing symbol (symbol table is not merged,
      yet) and see if it is a definition.  In fact we may also resolve aliases,
      but that is probably not too important.  */
   symtab_node *node = callee;
   for (int n = 10; node->previous_sharing_asm_name && n ; n--)
     node = node->previous_sharing_asm_name;
   if (node->previous_sharing_asm_name)
     node = symtab_node::get_for_asmname (DECL_ASSEMBLER_NAME (callee->decl));
   gcc_assert (TREE_PUBLIC (node->decl));
   return node->get_availability () >= AVAIL_INTERPOSABLE;
 }
 
+/* Return num_speculative_targets of this edge.  */
+
+int
+cgraph_edge::num_speculative_call_targets_p (void)
+{
+  return indirect_info ? indirect_info->num_speculative_call_targets : 0;
+}
+
 /* A stashed copy of "symtab" for use by selftest::symbol_table_test.
    This needs to be a global so that it can be a GC root, and thus
    prevent the stashed copy from being garbage-collected if the GC runs
    during a symbol_table_test.  */
 
 symbol_table *saved_symtab;
 
 #if CHECKING_P
 
 namespace selftest {
 
 /* class selftest::symbol_table_test.  */
 
 /* Constructor.  Store the old value of symtab, and create a new one.  */
 
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 9c086fedaef..4cb047776e9 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -1652,34 +1652,33 @@ class GTY(()) cgraph_indirect_call_info
 {
 public:
   /* When agg_content is set, an offset where the call pointer is located
      within the aggregate.  */
   HOST_WIDE_INT offset;
   /* Context of the polymorphic call; use only when POLYMORPHIC flag is set.  */
   ipa_polymorphic_call_context context;
   /* OBJ_TYPE_REF_TOKEN of a polymorphic call (if polymorphic is set).  */
   HOST_WIDE_INT otr_token;
   /* Type of the object from OBJ_TYPE_REF_OBJECT. */
   tree otr_type;
   /* Index of the parameter that is called.  */
   int param_index;
   /* ECF flags determined from the caller.  */
   int ecf_flags;
-  /* Profile_id of common target obtained from profile.  */
-  int common_target_id;
-  /* Probability that call will land in function with COMMON_TARGET_ID.  */
-  int common_target_probability;
+
+  /* Number of speculative call targets, it's less than GCOV_TOPN_VALUES.  */
+  unsigned num_speculative_call_targets : 16;
 
   /* Set when the call is a virtual call with the parameter being the
      associated object pointer rather than a simple direct call.  */
   unsigned polymorphic : 1;
   /* Set when the call is a call of a pointer loaded from contents of an
      aggregate at offset.  */
   unsigned agg_contents : 1;
   /* Set when this is a call through a member pointer.  */
   unsigned member_ptr : 1;
   /* When the agg_contents bit is set, this one determines whether the
      destination is loaded from a parameter passed by reference. */
   unsigned by_ref : 1;
   /* When the agg_contents bit is set, this one determines whether we can
      deduce from the function body that the loaded value from the reference is
      never modified between the invocation of the function and the load
@@ -1712,31 +1711,32 @@ public:
   /* If the edge does not lead to a thunk, simply redirect it to N.  Otherwise
      create one or more equivalent thunks for N and redirect E to the first in
      the chain.  Note that it is then necessary to call
      n->expand_all_artificial_thunks once all callers are redirected.  */
   void redirect_callee_duplicating_thunks (cgraph_node *n);
 
   /* Make an indirect edge with an unknown callee an ordinary edge leading to
      CALLEE.  DELTA is an integer constant that is to be added to the this
      pointer (first parameter) to compensate for skipping
      a thunk adjustment.  */
   cgraph_edge *make_direct (cgraph_node *callee);
 
   /* Turn edge into speculative call calling N2. Update
      the profile so the direct call is taken COUNT times
      with FREQUENCY.  */
-  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count);
+  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count,
+				 unsigned int speculative_id = 0);
 
    /* Given speculative call edge, return all three components.  */
   void speculative_call_info (cgraph_edge *&direct, cgraph_edge *&indirect,
 			      ipa_ref *&reference);
 
   /* Speculative call edge turned out to be direct call to CALLEE_DECL.
      Remove the speculative call sequence and return edge representing the call.
      It is up to caller to redirect the call as appropriate. */
   cgraph_edge *resolve_speculation (tree callee_decl = NULL);
 
   /* If necessary, change the function declaration in the call statement
      associated with the edge so that it corresponds to the edge callee.  */
   gimple *redirect_call_stmt_to_callee (void);
 
   /* Create clone of edge in the node N represented
@@ -1771,49 +1771,55 @@ public:
     return m_summary_id;
   }
 
   /* Rebuild cgraph edges for current function node.  This needs to be run after
      passes that don't update the cgraph.  */
   static unsigned int rebuild_edges (void);
 
   /* Rebuild cgraph references for current function node.  This needs to be run
      after passes that don't update the cgraph.  */
   static void rebuild_references (void);
 
   /* During LTO stream in this can be used to check whether call can possibly
      be internal to the current translation unit.  */
   bool possibly_call_in_translation_unit_p (void);
 
+  /* Return num_speculative_targets of this edge.  */
+  int num_speculative_call_targets_p (void);
+
   /* Expected number of executions: calculated in profile.c.  */
   profile_count count;
   cgraph_node *caller;
   cgraph_node *callee;
   cgraph_edge *prev_caller;
   cgraph_edge *next_caller;
   cgraph_edge *prev_callee;
   cgraph_edge *next_callee;
   gcall *call_stmt;
   /* Additional information about an indirect call.  Not cleared when an edge
      becomes direct.  */
   cgraph_indirect_call_info *indirect_info;
   PTR GTY ((skip (""))) aux;
   /* When equal to CIF_OK, inline this call.  Otherwise, points to the
      explanation why function was not inlined.  */
   enum cgraph_inline_failed_t inline_failed;
   /* The stmt_uid of call_stmt.  This is used by LTO to recover the call_stmt
      when the function is serialized in.  */
   unsigned int lto_stmt_uid;
+  /* speculative id is used to link direct calls with their corresponding
+     IPA_REF_ADDR references when representing speculative calls.  */
+  unsigned int speculative_id : 16;
   /* Whether this edge was made direct by indirect inlining.  */
   unsigned int indirect_inlining_edge : 1;
   /* Whether this edge describes an indirect call with an undetermined
      callee.  */
   unsigned int indirect_unknown_callee : 1;
   /* Whether this edge is still a dangling  */
   /* True if the corresponding CALL stmt cannot be inlined.  */
   unsigned int call_stmt_cannot_inline_p : 1;
   /* Can this call throw externally?  */
   unsigned int can_throw_external : 1;
   /* Edges with SPECULATIVE flag represents indirect calls that was
      speculatively turned into direct (i.e. by profile feedback).
      The final code sequence will have form:
 
      if (call_target == expected_fn)
diff --git a/gcc/cgraphclones.c b/gcc/cgraphclones.c
index 81c5dfd194f..da9a4f3f6f5 100644
--- a/gcc/cgraphclones.c
+++ b/gcc/cgraphclones.c
@@ -121,30 +121,31 @@ cgraph_edge::clone (cgraph_node *n, gcall *call_stmt, unsigned stmt_uid,
     }
   else
     {
       new_edge = n->create_edge (callee, call_stmt, prof_count, true);
       if (indirect_info)
 	{
 	  new_edge->indirect_info
 	    = ggc_cleared_alloc<cgraph_indirect_call_info> ();
 	  *new_edge->indirect_info = *indirect_info;
 	}
     }
 
   new_edge->inline_failed = inline_failed;
   new_edge->indirect_inlining_edge = indirect_inlining_edge;
   new_edge->lto_stmt_uid = stmt_uid;
+  new_edge->speculative_id = speculative_id;
   /* Clone flags that depend on call_stmt availability manually.  */
   new_edge->can_throw_external = can_throw_external;
   new_edge->call_stmt_cannot_inline_p = call_stmt_cannot_inline_p;
   new_edge->speculative = speculative;
   new_edge->in_polymorphic_cdtor = in_polymorphic_cdtor;
 
   /* Update IPA profile.  Local profiles need no updating in original.  */
   if (update_original)
     count = count.combine_with_ipa_count_within (count.ipa () 
 						 - new_edge->count.ipa (),
 						 caller->count);
   symtab->call_edge_duplication_hooks (this, new_edge);
   return new_edge;
 }
 
diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
index 4b28b94aaad..17ab5ce60d7 100644
--- a/gcc/ipa-profile.c
+++ b/gcc/ipa-profile.c
@@ -147,144 +147,417 @@ dump_histogram (FILE *file, vec<histogram_entry *> histogram)
   if (!overall_size)
     overall_size = 1;
   for (i = 0; i < histogram.length (); i++)
     {
       cumulated_time += histogram[i]->count * histogram[i]->time;
       cumulated_size += histogram[i]->size;
       fprintf (file, "  %" PRId64": time:%i (%2.2f) size:%i (%2.2f)\n",
 	       (int64_t) histogram[i]->count,
 	       histogram[i]->time,
 	       cumulated_time * 100.0 / overall_time,
 	       histogram[i]->size,
 	       cumulated_size * 100.0 / overall_size);
    }
 }
 
-/* Collect histogram from CFG profiles.  */
+/* Structure containing speculative target information from profile.  */
+
+struct GTY (()) speculative_call_target
+{
+  speculative_call_target (unsigned int id, int prob)
+    : target_id (id), target_probability (prob)
+  {
+  }
+
+  /* Profile_id of target obtained from profile.  */
+  unsigned int target_id;
+  /* Probability that call will land in function with target_id.  */
+  int target_probability;
+};
+
+class GTY ((for_user)) speculative_call_summary
+{
+public:
+  speculative_call_summary () : speculative_call_targets ()
+  {}
+
+  vec<speculative_call_target, va_gc> *speculative_call_targets;
+  ~speculative_call_summary ();
+
+  void dump (FILE *f);
+
+  /* Check whether this is a empty summary.  */
+  bool is_empty ();
+};
+
+  /* Class to manage call summaries.  */
+
+class GTY ((user)) ipa_profile_call_summaries
+  : public call_summary<speculative_call_summary *>
+{
+public:
+  ipa_profile_call_summaries (symbol_table *table, bool ggc)
+    : call_summary<speculative_call_summary *> (table, ggc)
+  {}
+
+  /* Duplicate info when an edge is cloned.  */
+  virtual void duplicate (cgraph_edge *, cgraph_edge *,
+			  speculative_call_summary *old_sum,
+			  speculative_call_summary *new_sum);
+};
+
+static GTY (()) ipa_profile_call_summaries *call_sums = NULL;
+
+speculative_call_summary::~speculative_call_summary ()
+{
+  if (speculative_call_targets)
+    {
+      vec_free (speculative_call_targets);
+      speculative_call_targets = NULL;
+    }
+}
+
+/* Dump all information in speculative call summary to F.  */
+
+void
+speculative_call_summary::dump (FILE *f)
+{
+  speculative_call_target *item;
+  cgraph_node *n2;
+  unsigned int i;
+
+  FOR_EACH_VEC_SAFE_ELT (speculative_call_targets, i, item)
+    {
+      n2 = find_func_by_profile_id (item->target_id);
+      if (n2)
+	fprintf (f, "    The %i speculative target is %s with prob %3.2f\n", i,
+		 n2->dump_name (),
+		 item->target_probability / (float) REG_BR_PROB_BASE);
+      else
+	fprintf (f, "    The %i speculative target is %u with prob %3.2f\n", i,
+		 item->target_id,
+		 item->target_probability / (float) REG_BR_PROB_BASE);
+    }
+}
+
+/* Check whether this is a empty summary.  */
+bool
+speculative_call_summary::is_empty ()
+{
+  return speculative_call_targets == NULL
+	 || speculative_call_targets->is_empty ();
+}
+
+/* Duplicate info when an edge is cloned.  */
+
+void
+ipa_profile_call_summaries::duplicate (cgraph_edge *, cgraph_edge *,
+				  speculative_call_summary *old_sum,
+				  speculative_call_summary *new_sum)
+{
+  if (!old_sum || !old_sum->speculative_call_targets)
+    return;
+
+  speculative_call_target *item;
+  unsigned int i;
+
+  FOR_EACH_VEC_SAFE_ELT (old_sum->speculative_call_targets, i, item)
+    {
+      vec_safe_push (new_sum->speculative_call_targets, *item);
+    }
+}
+
+/* Collect histogram and speculative target summaries from CFG profiles.  */
 
 static void
 ipa_profile_generate_summary (void)
 {
   struct cgraph_node *node;
   gimple_stmt_iterator gsi;
   basic_block bb;
 
   hash_table<histogram_hash> hashtable (10);
-  
+
+  gcc_checking_assert (!call_sums);
+  call_sums = (new (ggc_alloc_no_dtor<ipa_profile_call_summaries> ())
+		 ipa_profile_call_summaries (symtab, true));
+
   FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
     if (ENTRY_BLOCK_PTR_FOR_FN (DECL_STRUCT_FUNCTION (node->decl))->count.ipa_p ())
       FOR_EACH_BB_FN (bb, DECL_STRUCT_FUNCTION (node->decl))
 	{
 	  int time = 0;
 	  int size = 0;
 	  for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
 	    {
 	      gimple *stmt = gsi_stmt (gsi);
 	      if (gimple_code (stmt) == GIMPLE_CALL
 		  && !gimple_call_fndecl (stmt))
 		{
 		  histogram_value h;
 		  h = gimple_histogram_value_of_type
 			(DECL_STRUCT_FUNCTION (node->decl),
 			 stmt, HIST_TYPE_INDIR_CALL);
 		  /* No need to do sanity check: gimple_ic_transform already
 		     takes away bad histograms.  */
 		  if (h)
 		    {
 		      gcov_type val, count, all;
-		      if (get_nth_most_common_value (NULL, "indirect call", h,
-						     &val, &count, &all))
+		      struct cgraph_edge *e = node->get_edge (stmt);
+		      if (e && !e->indirect_unknown_callee)
+			continue;
+
+		      speculative_call_summary *csum
+			= call_sums->get_create (e);
+
+		      for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
 			{
-			  struct cgraph_edge * e = node->get_edge (stmt);
-			  if (e && !e->indirect_unknown_callee)
+			  if (!get_nth_most_common_value (NULL, "indirect call",
+							  h, &val, &count, &all,
+							  j))
 			    continue;
 
-			  e->indirect_info->common_target_id = val;
-			  e->indirect_info->common_target_probability
-			    = GCOV_COMPUTE_SCALE (count, all);
-			  if (e->indirect_info->common_target_probability > REG_BR_PROB_BASE)
+			  if (val == 0)
+			    continue;
+
+			  speculative_call_target item (
+			    val, GCOV_COMPUTE_SCALE (count, all));
+			  if (item.target_probability > REG_BR_PROB_BASE)
 			    {
 			      if (dump_file)
-				fprintf (dump_file, "Probability capped to 1\n");
-			      e->indirect_info->common_target_probability = REG_BR_PROB_BASE;
+				fprintf (dump_file,
+					 "Probability capped to 1\n");
+			      item.target_probability = REG_BR_PROB_BASE;
 			    }
+			  vec_safe_push (csum->speculative_call_targets, item);
 			}
+
 		      gimple_remove_histogram_value (DECL_STRUCT_FUNCTION (node->decl),
 						      stmt, h);
 		    }
 		}
 	      time += estimate_num_insns (stmt, &eni_time_weights);
 	      size += estimate_num_insns (stmt, &eni_size_weights);
 	    }
 	  if (bb->count.ipa_p () && bb->count.initialized_p ())
 	    account_time_size (&hashtable, histogram, bb->count.ipa ().to_gcov_type (),
 			       time, size);
 	}
   histogram.qsort (cmp_counts);
 }
 
+/* Serialize the speculative summary info for LTO.  */
+
+static void
+ipa_profile_write_edge_summary (lto_simple_output_block *ob,
+				speculative_call_summary *csum)
+{
+  speculative_call_target *item;
+  unsigned int i;
+  unsigned len = 0;
+
+  if (!csum->is_empty ())
+    len = csum->speculative_call_targets->length ();
+
+  gcc_assert (len <= GCOV_TOPN_VALUES);
+
+  streamer_write_hwi_stream (ob->main_stream, len);
+
+  if (len)
+    {
+      FOR_EACH_VEC_SAFE_ELT (csum->speculative_call_targets, i, item)
+	{
+	  gcc_assert (item->target_id);
+	  streamer_write_hwi_stream (ob->main_stream, item->target_id);
+	  streamer_write_hwi_stream (ob->main_stream, item->target_probability);
+	}
+    }
+}
+
 /* Serialize the ipa info for lto.  */
 
 static void
 ipa_profile_write_summary (void)
 {
   struct lto_simple_output_block *ob
     = lto_create_simple_output_block (LTO_section_ipa_profile);
   unsigned int i;
 
   streamer_write_uhwi_stream (ob->main_stream, histogram.length ());
   for (i = 0; i < histogram.length (); i++)
     {
       streamer_write_gcov_count_stream (ob->main_stream, histogram[i]->count);
       streamer_write_uhwi_stream (ob->main_stream, histogram[i]->time);
       streamer_write_uhwi_stream (ob->main_stream, histogram[i]->size);
     }
+
+  if (!call_sums)
+    return;
+
+  /* Serialize speculative targets information.  */
+  unsigned int count = 0;
+  lto_symtab_encoder_t encoder = ob->decl_state->symtab_node_encoder;
+  lto_symtab_encoder_iterator lsei;
+  cgraph_node *node;
+
+  for (lsei = lsei_start_function_in_partition (encoder); !lsei_end_p (lsei);
+       lsei_next_function_in_partition (&lsei))
+    {
+      node = lsei_cgraph_node (lsei);
+      if (node->definition && node->has_gimple_body_p ()
+	  && node->indirect_calls)
+	count++;
+    }
+
+  streamer_write_uhwi_stream (ob->main_stream, count);
+
+  /* Process all of the functions.  */
+  for (lsei = lsei_start_function_in_partition (encoder);
+       !lsei_end_p (lsei) && count; lsei_next_function_in_partition (&lsei))
+    {
+      cgraph_node *node = lsei_cgraph_node (lsei);
+      if (node->definition && node->has_gimple_body_p ()
+	  && node->indirect_calls)
+	{
+	  int node_ref = lto_symtab_encoder_encode (encoder, node);
+	  streamer_write_uhwi_stream (ob->main_stream, node_ref);
+
+	  for (cgraph_edge *e = node->indirect_calls; e; e = e->next_callee)
+	    {
+	      speculative_call_summary *csum = call_sums->get_create (e);
+	      ipa_profile_write_edge_summary (ob, csum);
+	    }
+      }
+    }
+
   lto_destroy_simple_output_block (ob);
 }
 
-/* Deserialize the ipa info for lto.  */
+/* Dump all profile summary data for all cgraph nodes and edges to file F.  */
+
+static void
+ipa_profile_dump_all_summaries (FILE *f)
+{
+  fprintf (dump_file,
+	   "\n========== IPA-profile speculative targets: ==========\n");
+  cgraph_node *node;
+  FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
+    {
+      fprintf (f, "\nSummary for node %s:\n", node->dump_name ());
+      for (cgraph_edge *e = node->indirect_calls; e; e = e->next_callee)
+	{
+	  fprintf (f, "  Summary for %s of indirect edge %d:\n",
+		   e->caller->dump_name (), e->lto_stmt_uid);
+	  speculative_call_summary *csum = call_sums->get_create (e);
+	  if (!csum->is_empty ())
+	    csum->dump (f);
+	  else
+	    fprintf (f, "    No indirect call summary.\n");
+	}
+    }
+  fprintf (f, "\n\n");
+}
+
+/* Read speculative targets information about edge for LTO WPA.  */
+
+static void
+ipa_profile_read_edge_summary (class lto_input_block *ib, cgraph_edge *edge)
+{
+  unsigned i, len;
+
+  len = streamer_read_hwi (ib);
+  gcc_assert (len <= GCOV_TOPN_VALUES);
+
+  speculative_call_summary *csum = call_sums->get_create (edge);
+
+  for (i = 0; i < len; i++)
+  {
+    speculative_call_target item (streamer_read_hwi (ib),
+	streamer_read_hwi (ib));
+    vec_safe_push (csum->speculative_call_targets, item);
+  }
+}
+
+/* Read profile speculative targets section information for LTO WPA.  */
+
+static void
+ipa_profile_read_summary_section (struct lto_file_decl_data *file_data,
+				  class lto_input_block *ib)
+{
+  if (!ib)
+    return;
+
+  lto_symtab_encoder_t encoder = file_data->symtab_node_encoder;
+
+  unsigned int count = streamer_read_uhwi (ib);
+
+  unsigned int i;
+  unsigned int index;
+  cgraph_node * node;
+
+  for (i = 0; i < count; i++)
+    {
+      index = streamer_read_uhwi (ib);
+      encoder = file_data->symtab_node_encoder;
+      node
+	= dyn_cast<cgraph_node *> (lto_symtab_encoder_deref (encoder, index));
+
+      for (cgraph_edge *e = node->indirect_calls; e; e = e->next_callee)
+	ipa_profile_read_edge_summary (ib, e);
+    }
+}
+
+/* Deserialize the IPA histogram and speculative targets summary info for LTO.
+   */
 
 static void
 ipa_profile_read_summary (void)
 {
   struct lto_file_decl_data ** file_data_vec
     = lto_get_file_decl_data ();
   struct lto_file_decl_data * file_data;
   int j = 0;
 
   hash_table<histogram_hash> hashtable (10);
 
+  gcc_checking_assert (!call_sums);
+  call_sums = (new (ggc_alloc_no_dtor<ipa_profile_call_summaries> ())
+		 ipa_profile_call_summaries (symtab, true));
+
   while ((file_data = file_data_vec[j++]))
     {
       const char *data;
       size_t len;
       class lto_input_block *ib
 	= lto_create_simple_input_block (file_data,
 					 LTO_section_ipa_profile,
 					 &data, &len);
       if (ib)
 	{
           unsigned int num = streamer_read_uhwi (ib);
 	  unsigned int n;
 	  for (n = 0; n < num; n++)
 	    {
 	      gcov_type count = streamer_read_gcov_count (ib);
 	      int time = streamer_read_uhwi (ib);
 	      int size = streamer_read_uhwi (ib);
 	      account_time_size (&hashtable, histogram,
 				 count, time, size);
 	    }
+
+	  ipa_profile_read_summary_section (file_data, ib);
+
 	  lto_destroy_simple_input_block (file_data,
 					  LTO_section_ipa_profile,
 					  ib, data, len);
 	}
     }
   histogram.qsort (cmp_counts);
 }
 
 /* Data used by ipa_propagate_frequency.  */
 
 struct ipa_propagate_frequency_data
 {
   cgraph_node *function_symbol;
   bool maybe_unlikely_executed;
   bool maybe_executed_once;
@@ -500,46 +773,45 @@ check_argument_count (struct cgraph_node *n, struct cgraph_edge *e)
 /* Simple ipa profile pass propagating frequencies across the callgraph.  */
 
 static unsigned int
 ipa_profile (void)
 {
   struct cgraph_node **order;
   struct cgraph_edge *e;
   int order_pos;
   bool something_changed = false;
   int i;
   gcov_type overall_time = 0, cutoff = 0, cumulated = 0, overall_size = 0;
   struct cgraph_node *n,*n2;
   int nindirect = 0, ncommon = 0, nunknown = 0, nuseless = 0, nconverted = 0;
   int nmismatch = 0, nimpossible = 0;
   bool node_map_initialized = false;
+  gcov_type threshold;
 
   if (dump_file)
     dump_histogram (dump_file, histogram);
   for (i = 0; i < (int)histogram.length (); i++)
     {
       overall_time += histogram[i]->count * histogram[i]->time;
       overall_size += histogram[i]->size;
     }
+  threshold = 0;
   if (overall_time)
     {
-      gcov_type threshold;
-
       gcc_assert (overall_size);
 
       cutoff = (overall_time * param_hot_bb_count_ws_permille + 500) / 1000;
-      threshold = 0;
       for (i = 0; cumulated < cutoff; i++)
 	{
 	  cumulated += histogram[i]->count * histogram[i]->time;
           threshold = histogram[i]->count;
 	}
       if (!threshold)
 	threshold = 1;
       if (dump_file)
 	{
 	  gcov_type cumulated_time = 0, cumulated_size = 0;
 
           for (i = 0;
 	       i < (int)histogram.length () && histogram[i]->count >= threshold;
 	       i++)
 	    {
@@ -551,65 +823,101 @@ ipa_profile (void)
 		   (int64_t)threshold,
 		   cumulated_time * 100.0 / overall_time,
 		   cumulated_size * 100.0 / overall_size);
 	}
 
       if (in_lto_p)
 	{
 	  if (dump_file)
 	    fprintf (dump_file, "Setting hotness threshold in LTO mode.\n");
           set_hot_bb_threshold (threshold);
 	}
     }
   histogram.release ();
   histogram_pool.release ();
 
-  /* Produce speculative calls: we saved common target from porfiling into
-     e->common_target_id.  Now, at link time, we can look up corresponding
+  /* Produce speculative calls: we saved common target from profiling into
+     e->target_id.  Now, at link time, we can look up corresponding
      function node and produce speculative call.  */
 
+  gcc_checking_assert (call_sums);
+
+  if (dump_file)
+    {
+      if (!node_map_initialized)
+	init_node_map (false);
+      node_map_initialized = true;
+
+      ipa_profile_dump_all_summaries (dump_file);
+    }
+
   FOR_EACH_DEFINED_FUNCTION (n)
     {
       bool update = false;
 
       if (!opt_for_fn (n->decl, flag_ipa_profile))
 	continue;
 
       for (e = n->indirect_calls; e; e = e->next_callee)
 	{
 	  if (n->count.initialized_p ())
 	    nindirect++;
-	  if (e->indirect_info->common_target_id)
+
+	  speculative_call_summary *csum = call_sums->get_create (e);
+	  if (!csum->is_empty ())
 	    {
 	      if (!node_map_initialized)
-	        init_node_map (false);
+		init_node_map (false);
 	      node_map_initialized = true;
 	      ncommon++;
-	      n2 = find_func_by_profile_id (e->indirect_info->common_target_id);
+
+	      if (in_lto_p)
+		{
+		  if (dump_file)
+		    {
+		      fprintf (dump_file,
+			       "Updating hotness threshold in LTO mode.\n");
+		      fprintf (dump_file, "Updated min count: %" PRId64 "\n",
+			       (int64_t) threshold
+				 / csum->speculative_call_targets->length ());
+		    }
+		  set_hot_bb_threshold (threshold
+		    / csum->speculative_call_targets->length ());
+		}
+
+	      unsigned speculative_id = 0;
+	      speculative_call_target *item;
+	      /* The code below is not formatted yet for review convenience.
+		 Move to a seprate small function is not easy as too many local
+		 variables used in it.  Need format and remove this comments
+		 once got approved.  */
+	      FOR_EACH_VEC_SAFE_ELT (csum->speculative_call_targets, i, item)
+	       {
+	      bool speculative_found = false;
+	      n2 = find_func_by_profile_id (item->target_id);
 	      if (n2)
 		{
 		  if (dump_file)
 		    {
 		      fprintf (dump_file, "Indirect call -> direct call from"
 			       " other module %s => %s, prob %3.2f\n",
 			       n->dump_name (),
 			       n2->dump_name (),
-			       e->indirect_info->common_target_probability
-			       / (float)REG_BR_PROB_BASE);
+			       item->target_probability
+				 / (float) REG_BR_PROB_BASE);
 		    }
-		  if (e->indirect_info->common_target_probability
-		      < REG_BR_PROB_BASE / 2)
+		  if (item->target_probability < REG_BR_PROB_BASE / 2)
 		    {
 		      nuseless++;
 		      if (dump_file)
 			fprintf (dump_file,
 				 "Not speculating: probability is too low.\n");
 		    }
 		  else if (!e->maybe_hot_p ())
 		    {
 		      nuseless++;
 		      if (dump_file)
 			fprintf (dump_file,
 				 "Not speculating: call is cold.\n");
 		    }
 		  else if (n2->get_availability () <= AVAIL_INTERPOSABLE
 			   && n2->can_be_discarded_p ())
@@ -641,44 +949,58 @@ ipa_profile (void)
 		    }
 		  else
 		    {
 		      /* Target may be overwritable, but profile says that
 			 control flow goes to this particular implementation
 			 of N2.  Speculate on the local alias to allow inlining.
 		       */
 		      if (!n2->can_be_discarded_p ())
 			{
 			  cgraph_node *alias;
 			  alias = dyn_cast<cgraph_node *> (n2->noninterposable_alias ());
 			  if (alias)
 			    n2 = alias;
 			}
 		      nconverted++;
-		      e->make_speculative
-			(n2,
-			 e->count.apply_probability
-				     (e->indirect_info->common_target_probability));
+		      e->make_speculative (n2,
+					   e->count.apply_probability (
+					     item->target_probability),
+					   speculative_id);
 		      update = true;
+		      speculative_id++;
+		      speculative_found = true;
 		    }
 		}
 	      else
 		{
 		  if (dump_file)
 		    fprintf (dump_file, "Function with profile-id %i not found.\n",
-			     e->indirect_info->common_target_id);
+			     item->target_id);
 		  nunknown++;
 		}
+	      if (!speculative_found && !csum->is_empty ())
+		{
+		  /* Remove item from speculative_call_targets if no
+		     speculative edge generated, rollback the iteration.  */
+		  csum->speculative_call_targets->ordered_remove (i);
+		  if (i)
+		    i--;
+		}
+	       }
+	      if (!csum->is_empty ())
+		e->indirect_info->num_speculative_call_targets
+		  = csum->speculative_call_targets->length ();
 	    }
 	 }
        if (update)
 	 ipa_update_overall_fn_summary (n);
      }
   if (node_map_initialized)
     del_node_map ();
   if (dump_file && nindirect)
     fprintf (dump_file,
 	     "%i indirect calls trained.\n"
 	     "%i (%3.2f%%) have common target.\n"
 	     "%i (%3.2f%%) targets was not found.\n"
 	     "%i (%3.2f%%) targets had parameter count mismatch.\n"
 	     "%i (%3.2f%%) targets was not in polymorphic call target list.\n"
 	     "%i (%3.2f%%) speculations seems useless.\n"
@@ -764,15 +1086,17 @@ public:
   {}
 
   /* opt_pass methods: */
   virtual bool gate (function *) { return flag_ipa_profile || in_lto_p; }
   virtual unsigned int execute (function *) { return ipa_profile (); }
 
 }; // class pass_ipa_profile
 
 } // anon namespace
 
 ipa_opt_pass_d *
 make_pass_ipa_profile (gcc::context *ctxt)
 {
   return new pass_ipa_profile (ctxt);
 }
+/* Tell the garbage collector about GTY markers in this source file.  */
+#include "gt-ipa-profile.h"
diff --git a/gcc/ipa-ref.h b/gcc/ipa-ref.h
index 00af24c77db..b68661d45c8 100644
--- a/gcc/ipa-ref.h
+++ b/gcc/ipa-ref.h
@@ -47,30 +47,31 @@ public:
   bool cannot_lead_to_return ();
 
   /* Return true if reference may be used in address compare.  */
   bool address_matters_p ();
 
   /* Return reference list this reference is in.  */
   struct ipa_ref_list * referring_ref_list (void);
 
   /* Return reference list this reference is in.  */
   struct ipa_ref_list * referred_ref_list (void);
 
   symtab_node *referring;
   symtab_node *referred;
   gimple *stmt;
   unsigned int lto_stmt_uid;
+  unsigned int speculative_id;
   unsigned int referred_index;
   ENUM_BITFIELD (ipa_ref_use) use:3;
   unsigned int speculative:1;
 };
 
 typedef struct ipa_ref ipa_ref_t;
 typedef struct ipa_ref *ipa_ref_ptr;
 
 
 /* List of references.  This is stored in both callgraph and varpool nodes.  */
 struct GTY(()) ipa_ref_list
 {
 public:
   /* Return first reference in list or NULL if empty.  */
   struct ipa_ref *first_reference (void)
diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
index b5221cd41f9..00a79d35a51 100644
--- a/gcc/lto-cgraph.c
+++ b/gcc/lto-cgraph.c
@@ -250,62 +250,58 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
   if (!edge->indirect_unknown_callee)
     {
       ref = lto_symtab_encoder_lookup (encoder, edge->callee);
       gcc_assert (ref != LCC_NOT_FOUND);
       streamer_write_hwi_stream (ob->main_stream, ref);
     }
 
   edge->count.stream_out (ob->main_stream);
 
   bp = bitpack_create (ob->main_stream);
   uid = (!gimple_has_body_p (edge->caller->decl) || edge->caller->thunk.thunk_p
 	 ? edge->lto_stmt_uid : gimple_uid (edge->call_stmt) + 1);
   bp_pack_enum (&bp, cgraph_inline_failed_t,
 	        CIF_N_REASONS, edge->inline_failed);
   bp_pack_var_len_unsigned (&bp, uid);
+  bp_pack_var_len_unsigned (&bp, edge->speculative_id);
   bp_pack_value (&bp, edge->indirect_inlining_edge, 1);
   bp_pack_value (&bp, edge->speculative, 1);
   bp_pack_value (&bp, edge->call_stmt_cannot_inline_p, 1);
   gcc_assert (!edge->call_stmt_cannot_inline_p
 	      || edge->inline_failed != CIF_BODY_NOT_AVAILABLE);
   bp_pack_value (&bp, edge->can_throw_external, 1);
   bp_pack_value (&bp, edge->in_polymorphic_cdtor, 1);
   if (edge->indirect_unknown_callee)
     {
       int flags = edge->indirect_info->ecf_flags;
       bp_pack_value (&bp, (flags & ECF_CONST) != 0, 1);
       bp_pack_value (&bp, (flags & ECF_PURE) != 0, 1);
       bp_pack_value (&bp, (flags & ECF_NORETURN) != 0, 1);
       bp_pack_value (&bp, (flags & ECF_MALLOC) != 0, 1);
       bp_pack_value (&bp, (flags & ECF_NOTHROW) != 0, 1);
       bp_pack_value (&bp, (flags & ECF_RETURNS_TWICE) != 0, 1);
       /* Flags that should not appear on indirect calls.  */
       gcc_assert (!(flags & (ECF_LOOPING_CONST_OR_PURE
 			     | ECF_MAY_BE_ALLOCA
 			     | ECF_SIBCALL
 			     | ECF_LEAF
 			     | ECF_NOVOPS)));
+
+      bp_pack_value (&bp, edge->indirect_info->num_speculative_call_targets,
+		     16);
     }
   streamer_write_bitpack (&bp);
-  if (edge->indirect_unknown_callee)
-    {
-      streamer_write_hwi_stream (ob->main_stream,
-			         edge->indirect_info->common_target_id);
-      if (edge->indirect_info->common_target_id)
-	streamer_write_hwi_stream
-	   (ob->main_stream, edge->indirect_info->common_target_probability);
-    }
 }
 
 /* Return if NODE contain references from other partitions.  */
 
 bool
 referenced_from_other_partition_p (symtab_node *node, lto_symtab_encoder_t encoder)
 {
   int i;
   struct ipa_ref *ref = NULL;
 
   for (i = 0; node->iterate_referring (i, ref); i++)
     {
       /* Ignore references from non-offloadable nodes while streaming NODE into
 	 offload LTO section.  */
       if (!ref->referring->need_lto_streaming)
@@ -678,30 +674,31 @@ lto_output_ref (struct lto_simple_output_block *ob, struct ipa_ref *ref,
 
   bp = bitpack_create (ob->main_stream);
   bp_pack_value (&bp, ref->use, 3);
   bp_pack_value (&bp, ref->speculative, 1);
   streamer_write_bitpack (&bp);
   nref = lto_symtab_encoder_lookup (encoder, ref->referred);
   gcc_assert (nref != LCC_NOT_FOUND);
   streamer_write_hwi_stream (ob->main_stream, nref);
   
   node = dyn_cast <cgraph_node *> (ref->referring);
   if (node)
     {
       if (ref->stmt)
 	uid = gimple_uid (ref->stmt) + 1;
       streamer_write_hwi_stream (ob->main_stream, uid);
+      streamer_write_hwi_stream (ob->main_stream, ref->speculative_id);
     }
 }
 
 /* Stream out profile_summary to OB.  */
 
 static void
 output_profile_summary (struct lto_simple_output_block *ob)
 {
   if (profile_info)
     {
       /* We do not output num and run_max, they are not used by
          GCC profile feedback and they are difficult to merge from multiple
          units.  */
       unsigned runs = (profile_info->runs);
       streamer_write_uhwi_stream (ob->main_stream, runs);
@@ -1416,99 +1413,104 @@ input_ref (class lto_input_block *ib,
 	   vec<symtab_node *> nodes)
 {
   symtab_node *node = NULL;
   struct bitpack_d bp;
   enum ipa_ref_use use;
   bool speculative;
   struct ipa_ref *ref;
 
   bp = streamer_read_bitpack (ib);
   use = (enum ipa_ref_use) bp_unpack_value (&bp, 3);
   speculative = (enum ipa_ref_use) bp_unpack_value (&bp, 1);
   node = nodes[streamer_read_hwi (ib)];
   ref = referring_node->create_reference (node, use);
   ref->speculative = speculative;
   if (is_a <cgraph_node *> (referring_node))
-    ref->lto_stmt_uid = streamer_read_hwi (ib);
+    {
+      ref->lto_stmt_uid = streamer_read_hwi (ib);
+      ref->speculative_id = streamer_read_hwi (ib);
+    }
 }
 
 /* Read an edge from IB.  NODES points to a vector of previously read nodes for
    decoding caller and callee of the edge to be read.  If INDIRECT is true, the
    edge being read is indirect (in the sense that it has
    indirect_unknown_callee set).  */
 
 static void
 input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
 	    bool indirect)
 {
   struct cgraph_node *caller, *callee;
   struct cgraph_edge *edge;
-  unsigned int stmt_id;
+  unsigned int stmt_id, speculative_id;
   profile_count count;
   cgraph_inline_failed_t inline_failed;
   struct bitpack_d bp;
   int ecf_flags = 0;
 
   caller = dyn_cast<cgraph_node *> (nodes[streamer_read_hwi (ib)]);
   if (caller == NULL || caller->decl == NULL_TREE)
     internal_error ("bytecode stream: no caller found while reading edge");
 
   if (!indirect)
     {
       callee = dyn_cast<cgraph_node *> (nodes[streamer_read_hwi (ib)]);
       if (callee == NULL || callee->decl == NULL_TREE)
 	internal_error ("bytecode stream: no callee found while reading edge");
     }
   else
     callee = NULL;
 
   count = profile_count::stream_in (ib);
 
   bp = streamer_read_bitpack (ib);
   inline_failed = bp_unpack_enum (&bp, cgraph_inline_failed_t, CIF_N_REASONS);
   stmt_id = bp_unpack_var_len_unsigned (&bp);
+  speculative_id = bp_unpack_var_len_unsigned (&bp);
 
   if (indirect)
     edge = caller->create_indirect_edge (NULL, 0, count);
   else
     edge = caller->create_edge (callee, NULL, count);
 
   edge->indirect_inlining_edge = bp_unpack_value (&bp, 1);
   edge->speculative = bp_unpack_value (&bp, 1);
   edge->lto_stmt_uid = stmt_id;
+  edge->speculative_id = speculative_id;
   edge->inline_failed = inline_failed;
   edge->call_stmt_cannot_inline_p = bp_unpack_value (&bp, 1);
   edge->can_throw_external = bp_unpack_value (&bp, 1);
   edge->in_polymorphic_cdtor = bp_unpack_value (&bp, 1);
   if (indirect)
     {
       if (bp_unpack_value (&bp, 1))
 	ecf_flags |= ECF_CONST;
       if (bp_unpack_value (&bp, 1))
 	ecf_flags |= ECF_PURE;
       if (bp_unpack_value (&bp, 1))
 	ecf_flags |= ECF_NORETURN;
       if (bp_unpack_value (&bp, 1))
 	ecf_flags |= ECF_MALLOC;
       if (bp_unpack_value (&bp, 1))
 	ecf_flags |= ECF_NOTHROW;
       if (bp_unpack_value (&bp, 1))
 	ecf_flags |= ECF_RETURNS_TWICE;
       edge->indirect_info->ecf_flags = ecf_flags;
-      edge->indirect_info->common_target_id = streamer_read_hwi (ib);
-      if (edge->indirect_info->common_target_id)
-        edge->indirect_info->common_target_probability = streamer_read_hwi (ib);
+
+      edge->indirect_info->num_speculative_call_targets
+	= bp_unpack_value (&bp, 16);
     }
 }
 
 
 /* Read a cgraph from IB using the info in FILE_DATA.  */
 
 static vec<symtab_node *> 
 input_cgraph_1 (struct lto_file_decl_data *file_data,
 		class lto_input_block *ib)
 {
   enum LTO_symtab_tags tag;
   vec<symtab_node *> nodes = vNULL;
   symtab_node *node;
   unsigned i;
 
diff --git a/gcc/predict.c b/gcc/predict.c
index 67f850de17a..592db3421f3 100644
--- a/gcc/predict.c
+++ b/gcc/predict.c
@@ -749,31 +749,30 @@ dump_prediction (FILE *file, enum br_predictor predictor, int probability,
 	  fprintf (file, " hit ");
 	  e->count ().dump (file);
 	  fprintf (file, " (%.1f%%)", e->count ().to_gcov_type() * 100.0
 		   / bb->count.to_gcov_type ());
 	}
     }
 
   fprintf (file, "\n");
 
   /* Print output that be easily read by analyze_brprob.py script. We are
      interested only in counts that are read from GCDA files.  */
   if (dump_file && (dump_flags & TDF_DETAILS)
       && bb->count.precise_p ()
       && reason == REASON_NONE)
     {
-      gcc_assert (e->count ().precise_p ());
       fprintf (file, ";;heuristics;%s;%" PRId64 ";%" PRId64 ";%.1f;\n",
 	       predictor_info[predictor].name,
 	       bb->count.to_gcov_type (), e->count ().to_gcov_type (),
 	       probability * 100.0 / REG_BR_PROB_BASE);
     }
 }
 
 /* Return true if STMT is known to be unlikely executed.  */
 
 static bool
 unlikely_executed_stmt_p (gimple *stmt)
 {
   if (!is_gimple_call (stmt))
     return false;
   /* NORETURN attribute alone is not strong enough: exit() may be quite
diff --git a/gcc/symtab.c b/gcc/symtab.c
index f4317d02b71..c43bf613f08 100644
--- a/gcc/symtab.c
+++ b/gcc/symtab.c
@@ -591,30 +591,31 @@ symtab_node::create_reference (symtab_node *referred_node,
       ref->referred_index = 0;
 
       for (unsigned int i = 1; i < list2->referring.length (); i++)
 	list2->referring[i]->referred_index = i;
     }
   else
     {
       list2->referring.safe_push (ref);
       ref->referred_index = list2->referring.length () - 1;
     }
 
   ref->referring = this;
   ref->referred = referred_node;
   ref->stmt = stmt;
   ref->lto_stmt_uid = 0;
+  ref->speculative_id = 0;
   ref->use = use_type;
   ref->speculative = 0;
 
   /* If vector was moved in memory, update pointers.  */
   if (old_references != list->references->address ())
     {
       int i;
       for (i = 0; iterate_reference(i, ref2); i++)
 	ref2->referred_ref_list ()->referring[ref2->referred_index] = ref2;
     }
   return ref;
 }
 
 ipa_ref *
 symtab_node::maybe_create_reference (tree val, gimple *stmt)
@@ -648,63 +649,66 @@ symtab_node::maybe_create_reference (tree val, gimple *stmt)
 /* Clone all references from symtab NODE to this symtab_node.  */
 
 void
 symtab_node::clone_references (symtab_node *node)
 {
   ipa_ref *ref = NULL, *ref2 = NULL;
   int i;
   for (i = 0; node->iterate_reference (i, ref); i++)
     {
       bool speculative = ref->speculative;
       unsigned int stmt_uid = ref->lto_stmt_uid;
 
       ref2 = create_reference (ref->referred, ref->use, ref->stmt);
       ref2->speculative = speculative;
       ref2->lto_stmt_uid = stmt_uid;
+      ref2->speculative_id = ref->speculative_id;
     }
 }
 
 /* Clone all referring from symtab NODE to this symtab_node.  */
 
 void
 symtab_node::clone_referring (symtab_node *node)
 {
   ipa_ref *ref = NULL, *ref2 = NULL;
   int i;
   for (i = 0; node->iterate_referring(i, ref); i++)
     {
       bool speculative = ref->speculative;
       unsigned int stmt_uid = ref->lto_stmt_uid;
 
       ref2 = ref->referring->create_reference (this, ref->use, ref->stmt);
       ref2->speculative = speculative;
       ref2->lto_stmt_uid = stmt_uid;
+      ref2->speculative_id = ref->speculative_id;
     }
 }
 
 /* Clone reference REF to this symtab_node and set its stmt to STMT.  */
 
 ipa_ref *
 symtab_node::clone_reference (ipa_ref *ref, gimple *stmt)
 {
   bool speculative = ref->speculative;
   unsigned int stmt_uid = ref->lto_stmt_uid;
   ipa_ref *ref2;
 
   ref2 = create_reference (ref->referred, ref->use, stmt);
   ref2->speculative = speculative;
   ref2->lto_stmt_uid = stmt_uid;
+  ref2->speculative_id = ref->speculative_id;
   return ref2;
 }
 
 /* Find the structure describing a reference to REFERRED_NODE
    and associated with statement STMT.  */
 
 ipa_ref *
 symtab_node::find_reference (symtab_node *referred_node,
 			     gimple *stmt, unsigned int lto_stmt_uid)
 {
   ipa_ref *r = NULL;
   int i;
 
   for (i = 0; iterate_reference (i, r); i++)
     if (r->referred == referred_node
@@ -735,30 +739,31 @@ symtab_node::remove_stmt_references (gimple *stmt)
    Those are not maintained during inlining & cloning.
    The exception are speculative references that are updated along
    with callgraph edges associated with them.  */
 
 void
 symtab_node::clear_stmts_in_references (void)
 {
   ipa_ref *r = NULL;
   int i;
 
   for (i = 0; iterate_reference (i, r); i++)
     if (!r->speculative)
       {
 	r->stmt = NULL;
 	r->lto_stmt_uid = 0;
+	r->speculative_id = 0;
       }
 }
 
 /* Remove all references in ref list.  */
 
 void
 symtab_node::remove_all_references (void)
 {
   while (vec_safe_length (ref_list.references))
     ref_list.references->last ().remove_reference ();
   vec_free (ref_list.references);
 }
 
 /* Remove all referring items in ref list.  */
 
diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
new file mode 100644
index 00000000000..a13b08cd60e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
@@ -0,0 +1,33 @@
+/* { dg-require-effective-target lto } */
+/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate" } */
+
+#include <stdio.h>
+
+typedef int (*fptr) (int);
+int
+one (int a);
+
+int
+two (int a);
+
+fptr table[] = {&one, &two};
+
+int
+main()
+{
+  int i, x;
+  fptr p = &one;
+
+  x = one (3);
+
+  for (i = 0; i < 350000000; i++)
+    {
+      x = (*p) (3);
+      p = table[x];
+    }
+  printf ("done:%d\n", x);
+}
+
+/* { dg-final-use-not-autofdo { scan-pgo-wpa-ipa-dump "2 \\(200.00%\\) speculations produced." "profile_estimate" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
new file mode 100644
index 00000000000..a8c6e365fb9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
@@ -0,0 +1,22 @@
+/* It seems there is no way to avoid the other source of mulitple
+   source testcase from being compiled independently.  Just avoid
+   error.  */
+#ifdef DOJOB
+int
+one (int a)
+{
+  return 1;
+}
+
+int
+two (int a)
+{
+  return 0;
+}
+#else
+int
+main()
+{
+  return 0;
+}
+#endif
diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
new file mode 100644
index 00000000000..9b996fcf0ed
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
@@ -0,0 +1,40 @@
+/* { dg-require-effective-target lto } */
+/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate" } */
+
+#include <stdio.h>
+
+typedef int (*fptr) (int);
+int
+one (int a);
+
+int
+two (int a);
+
+fptr table[] = {&one, &two};
+
+int foo ()
+{
+  int i, x;
+  fptr p = &one;
+
+  x = one (3);
+
+  for (i = 0; i < 350000000; i++)
+    {
+      x = (*p) (3);
+      p = table[x];
+    }
+  return x;
+}
+
+int
+main()
+{
+  int x = foo ();
+  printf ("done:%d\n", x);
+}
+
+/* { dg-final-use-not-autofdo { scan-pgo-wpa-ipa-dump "2 \\(200.00%\\) speculations produced." "profile_estimate" } } */
+
diff --git a/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
new file mode 100644
index 00000000000..063996c71df
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
@@ -0,0 +1,37 @@
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-options "-O2 -fdump-ipa-profile_estimate" } */
+
+#include <stdio.h>
+
+typedef int (*fptr) (int);
+int
+one (int a)
+{
+  return 1;
+}
+
+int
+two (int a)
+{
+  return 0;
+}
+
+fptr table[] = {&one, &two};
+
+int
+main()
+{
+  int i, x;
+  fptr p = &one;
+
+  one (3);
+
+  for (i = 0; i < 350000000; i++)
+    {
+      x = (*p) (3);
+      p = table[x];
+    }
+  printf ("done:%d\n", x);
+}
+
+/* { dg-final-use-not-autofdo { scan-ipa-dump "2 \\(200.00%\\) speculations produced." "profile_estimate" } } */
diff --git a/gcc/testsuite/lib/scandump.exp b/gcc/testsuite/lib/scandump.exp
index 42f5c01aa60..8b7cd7cfc16 100644
--- a/gcc/testsuite/lib/scandump.exp
+++ b/gcc/testsuite/lib/scandump.exp
@@ -58,30 +58,31 @@ proc scan-dump { args } {
         }
     }
 
     set testcase [testname-for-summary]
     # The name might include a list of options; extract the file name.
     set filename [lindex $testcase 0]
 
     set printable_pattern [make_pattern_printable [lindex $args 1]]
     set suf [dump-suffix [lindex $args 2]]
     set testname "$testcase scan-[lindex $args 0]-dump $suf \"$printable_pattern\""
     set src [file tail $filename]
     set dumpbase [dump-base $src [lindex $args 3]]
     set output_file "[glob -nocomplain $dumpbase.[lindex $args 2]]"
     if { $output_file == "" } {
 	verbose -log "$testcase: dump file does not exist"
+	verbose -log "dump file: $dumpbase.$suf"
 	unresolved "$testname"
 	return
     }
 
     set fd [open $output_file r]
     set text [read $fd]
     close $fd
 
     if [regexp -- [lindex $args 1] $text] {
 	pass "$testname"
     } else {
 	fail "$testname"
     }
 }
 
diff --git a/gcc/testsuite/lib/scanwpaipa.exp b/gcc/testsuite/lib/scanwpaipa.exp
index b5549fd688e..8aafd6c82e8 100644
--- a/gcc/testsuite/lib/scanwpaipa.exp
+++ b/gcc/testsuite/lib/scanwpaipa.exp
@@ -33,30 +33,53 @@ proc scan-wpa-ipa-dump { args } {
     }
     if { [llength $args] > 3 } {
 	error "scan-wpa-ipa-dump: too many arguments"
 	return
     }
     if { [llength $args] >= 3 } {
 	scan-dump "wpa-ipa" [lindex $args 0] \
 		  "\[0-9\]\[0-9\]\[0-9\]i.[lindex $args 1]" ".exe.wpa" \
 		  [lindex $args 2]
     } else {
 	scan-dump "wpa-ipa" [lindex $args 0] \
 		  "\[0-9\]\[0-9\]\[0-9\]i.[lindex $args 1]" ".exe.wpa"
     }
 }
 
+# Argument 0 is the regexp to match
+# Argument 1 is the name of the dumped ipa pass
+# Argument 2 handles expected failures and the like
+proc scan-pgo-wpa-ipa-dump { args } {
+
+    if { [llength $args] < 2 } {
+	error "scan-pgo-wpa-ipa-dump: too few arguments"
+	return
+    }
+    if { [llength $args] > 3 } {
+	error "scan-pgo-wpa-ipa-dump: too many arguments"
+	return
+    }
+    if { [llength $args] >= 3 } {
+	scan-dump "pgo-wpa-ipa" [lindex $args 0] \
+		  "\[0-9\]\[0-9\]\[0-9\]i.[lindex $args 1]" ".x02.wpa" \
+		  [lindex $args 2]
+    } else {
+	scan-dump "pgo-wpa-ipa" [lindex $args 0] \
+		  "\[0-9\]\[0-9\]\[0-9\]i.[lindex $args 1]" ".x02.wpa"
+    }
+}
+
 # Call pass if pattern is present given number of times, otherwise fail.
 # Argument 0 is the regexp to match
 # Argument 1 is number of times the regexp must be found
 # Argument 2 is the name of the dumped ipa pass
 # Argument 3 handles expected failures and the like
 proc scan-wpa-ipa-dump-times { args } {
 
     if { [llength $args] < 3 } {
 	error "scan-wpa-ipa-dump-times: too few arguments"
 	return
     }
     if { [llength $args] > 4 } {
 	error "scan-wpa-ipa-dump-times: too many arguments"
 	return
     }
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index 720f50eefec..ee024388cf7 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -2185,30 +2185,52 @@ copy_bb (copy_body_data *id, basic_block bb,
 		      edge = edge->clone (id->dst_node, call_stmt,
 					  gimple_uid (stmt),
 					  num, den,
 					  true);
 
 		      /* Speculative calls consist of two edges - direct and
 			 indirect.  Duplicate the whole thing and distribute
 			 frequencies accordingly.  */
 		      if (edge->speculative)
 			{
 			  struct cgraph_edge *direct, *indirect;
 			  struct ipa_ref *ref;
 
 			  gcc_assert (!edge->indirect_unknown_callee);
 			  old_edge->speculative_call_info (direct, indirect, ref);
+			  while (old_edge->next_callee
+				 && old_edge->next_callee->speculative
+				 && indirect->num_speculative_call_targets_p ()
+				      > 1)
+			    {
+			      id->dst_node->clone_reference (ref, stmt);
+
+			      edge = old_edge->next_callee;
+			      edge = edge->clone (id->dst_node, call_stmt,
+						  gimple_uid (stmt), num, den,
+						  true);
+			      old_edge = old_edge->next_callee;
+			      gcc_assert (!edge->indirect_unknown_callee);
+
+			      /* If the indirect edge has multiple speculative
+				 calls, iterate through all direct calls
+				 associated to the speculative call and clone
+				 all related direct edges before cloning the
+				 related indirect edge.  */
+			      old_edge->speculative_call_info (direct, indirect,
+							       ref);
+			    }
 
 			  profile_count indir_cnt = indirect->count;
 			  indirect = indirect->clone (id->dst_node, call_stmt,
 						      gimple_uid (stmt),
 						      num, den,
 						      true);
 
 			  profile_probability prob
 			     = indir_cnt.probability_in (old_cnt + indir_cnt);
 			  indirect->count
 			     = copy_basic_block->count.apply_probability (prob);
 			  edge->count = copy_basic_block->count - indirect->count;
 			  id->dst_node->clone_reference (ref, stmt);
 			}
 		      else
diff --git a/gcc/value-prof.c b/gcc/value-prof.c
index cc3542f0295..f64f515c1ee 100644
--- a/gcc/value-prof.c
+++ b/gcc/value-prof.c
@@ -94,31 +94,31 @@ along with GCC; see the file COPYING3.  If not see
    Limitations / FIXME / TODO:
    * Only one histogram of each type can be associated with a statement.
    * Some value profile transformations are done in builtins.c (?!)
    * Updating of histograms needs some TLC.
    * The value profiling code could be used to record analysis results
      from non-profiling (e.g. VRP).
    * Adding new profilers should be simplified, starting with a cleanup
      of what-happens-where and with making gimple_find_values_to_profile
      and gimple_value_profile_transformations table-driven, perhaps...
 */
 
 static bool gimple_divmod_fixed_value_transform (gimple_stmt_iterator *);
 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 gimple_ic_transform (gimple_stmt_iterator *);
 
 /* Allocate histogram value.  */
 
 histogram_value
 gimple_alloc_histogram_value (struct function *fun ATTRIBUTE_UNUSED,
 			      enum hist_type type, gimple *stmt, tree value)
 {
    histogram_value hist = (histogram_value) xcalloc (1, sizeof (*hist));
    hist->hvalue.value = value;
    hist->hvalue.stmt = stmt;
    hist->type = type;
    return hist;
 }
 
 /* Hash value for histogram.  */
@@ -604,42 +604,44 @@ gimple_value_profile_transformations (void)
 	      fprintf (dump_file, "Trying transformations on stmt ");
 	      print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
 	      dump_histograms_for_stmt (cfun, dump_file, stmt);
 	    }
 
 	  /* Transformations:  */
 	  /* The order of things in this conditional controls which
 	     transformation is used when more than one is applicable.  */
 	  /* It is expected that any code added by the transformations
 	     will be added before the current statement, and that the
 	     current statement remain valid (although possibly
 	     modified) upon return.  */
 	  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;
 	      /* Original statement may no longer be in the same block. */
 	      if (bb != gimple_bb (stmt))
 		{
 	          bb = gimple_bb (stmt);
 		  gsi = gsi_for_stmt (stmt);
 		}
 	    }
+
+	  /* The function never thansforms a GIMPLE statement.  */
+	  gimple_ic_transform (&gsi);
         }
     }
 
   return changed;
 }
 
 /* Generate code for transformation 1 (with parent gimple assignment
    STMT and probability of taking the optimal path PROB, which is
    equivalent to COUNT/ALL within roundoff error).  This generates the
    result into a temp and returns the temp; it does not replace or
    alter the original STMT.  */
 
 static tree
 gimple_divmod_fixed_value (gassign *stmt, tree value, profile_probability prob,
 			   gcov_type count, gcov_type all)
@@ -1374,92 +1376,97 @@ gimple_ic (gcall *icall_stmt, struct cgraph_node *direct_call,
 	e = make_edge (dcall_bb, e_eh->dest, e_eh->flags);
 	e->probability = e_eh->probability;
 	for (gphi_iterator psi = gsi_start_phis (e_eh->dest);
 	     !gsi_end_p (psi); gsi_next (&psi))
 	  {
 	    gphi *phi = psi.phi ();
 	    SET_USE (PHI_ARG_DEF_PTR_FROM_EDGE (phi, e),
 		     PHI_ARG_DEF_FROM_EDGE (phi, e_eh));
 	  }
        }
   if (!stmt_could_throw_p (cfun, dcall_stmt))
     gimple_purge_dead_eh_edges (dcall_bb);
   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:
- */
+/* There maybe multiple indirect targets in histogram.  Check every
+   indirect/virtual call if callee function exists, if not exist, leave it to
+   LTO stage for later process.  Modify code of this indirect call to an if-else
+   structure in ipa-profile finally.  */
 
-static bool
+static void
 gimple_ic_transform (gimple_stmt_iterator *gsi)
 {
   gcall *stmt;
   histogram_value histogram;
   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;
+    return;
 
-  if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
-				  &count, &all))
-    return false;
+  count = 0;
+  all = histogram->hvalue.counters[0];
 
-  if (4 * count <= 3 * all)
-    return false;
+  for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
+    {
+      if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
+				      &count, &all, j))
+	return;
 
-  direct_call = find_func_by_profile_id ((int)val);
+      /* Minimum probability.  should be higher than 25%.  */
+      if (4 * count <= all)
+	return;
 
-  if (direct_call == NULL)
-    {
-      if (val)
+      direct_call = find_func_by_profile_id ((int) val);
+
+      if (direct_call == NULL)
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
-			     "Indirect call -> direct call from other "
-			     "module %T=> %i (will resolve only with LTO)\n",
-			     gimple_call_fn (stmt), (int)val);
+	  if (val)
+	    {
+	      if (dump_enabled_p ())
+		dump_printf_loc (
+		  MSG_MISSED_OPTIMIZATION, stmt,
+		  "Indirect call -> direct call from other "
+		  "module %T=> %i (will resolve only with LTO)\n",
+		  gimple_call_fn (stmt), (int) val);
+	    }
+	  return;
 	}
-      return false;
-    }
 
-  if (dump_enabled_p ())
-    {
-      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
-		       "Indirect call -> direct call "
-		       "%T => %T transformation on insn postponed\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_enabled_p ())
+	{
+	  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
+			   "Indirect call -> direct call "
+			   "%T => %T transformation on insn postponed\n",
+			   gimple_call_fn (stmt), direct_call->decl);
+	  dump_printf_loc (MSG_NOTE, stmt,
+			   "hist->count %" PRId64 " hist->all %" PRId64 "\n",
+			   count, all);
+	}
     }
-
-  return true;
 }
 
 /* Return true if the stringop CALL shall be profiled.  SIZE_ARG be
    set to the argument index for the size of the string operation.  */
 
 static bool
 interesting_stringop_to_profile_p (gcall *call, int *size_arg)
 {
   enum built_in_function fcode;
 
   fcode = DECL_FUNCTION_CODE (gimple_call_fndecl (call));
   switch (fcode)
     {
      case BUILT_IN_MEMCPY:
      case BUILT_IN_MEMPCPY:
diff --git a/gcc/value-prof.h b/gcc/value-prof.h
index 77c06f60096..b3eeb57d37d 100644
--- a/gcc/value-prof.h
+++ b/gcc/value-prof.h
@@ -77,31 +77,30 @@ histogram_value gimple_alloc_histogram_value (struct function *, enum hist_type,
 					      tree value = NULL_TREE);
 histogram_value gimple_histogram_value (struct function *, gimple *);
 histogram_value gimple_histogram_value_of_type (struct function *, gimple *,
 						enum hist_type);
 void gimple_add_histogram_value (struct function *, gimple *, histogram_value);
 void dump_histograms_for_stmt (struct function *, FILE *, gimple *);
 void gimple_remove_histogram_value (struct function *, gimple *, histogram_value);
 void gimple_remove_stmt_histograms (struct function *, gimple *);
 void gimple_duplicate_stmt_histograms (struct function *, gimple *,
 				       struct function *, gimple *);
 void gimple_move_stmt_histograms (struct function *, gimple *, gimple *);
 void verify_histograms (void);
 void free_histograms (function *);
 void stringop_block_profile (gimple *, unsigned int *, HOST_WIDE_INT *);
 gcall *gimple_ic (gcall *, struct cgraph_node *, profile_probability);
-bool check_ic_target (gcall *, struct cgraph_node *);
 bool get_nth_most_common_value (gimple *stmt, const char *counter_type,
 				histogram_value hist, gcov_type *value,
 				gcov_type *count, gcov_type *all,
 				unsigned n = 0);
 
 /* In tree-profile.c.  */
 extern void gimple_init_gcov_profiler (void);
 extern void gimple_gen_edge_profiler (int, edge);
 extern void gimple_gen_interval_profiler (histogram_value, unsigned);
 extern void gimple_gen_pow2_profiler (histogram_value, unsigned);
 extern void gimple_gen_topn_values_profiler (histogram_value, unsigned);
 extern void gimple_gen_ic_profiler (histogram_value, unsigned);
 extern void gimple_gen_ic_func_profiler (void);
 extern void gimple_gen_time_profiler (unsigned);
 extern void gimple_gen_average_profiler (histogram_value, unsigned);
-- 
2.21.0.777.g83232e3864


^ permalink raw reply	[flat|nested] 38+ messages in thread

* *Ping* Re: [PATCH v6] Missed function specialization + partial devirtualization
  2019-12-03  2:31                             ` [PATCH v6] " luoxhu
@ 2019-12-18  2:33                               ` luoxhu
  2019-12-19 13:22                                 ` Jan Hubicka
  0 siblings, 1 reply; 38+ messages in thread
From: luoxhu @ 2019-12-18  2:33 UTC (permalink / raw)
  To: Jan Hubicka, Martin Liška; +Cc: gcc-patches, segher, wschmidt

Ping :)

Patch is here:
https://gcc.gnu.org/ml/gcc-patches/2019-12/msg00099.html


On 2019/12/3 10:31, luoxhu wrote:
> Hi Martin and Honza,
> 
> 
> On 2019/11/18 21:02, Martin Liška wrote:
>> On 11/16/19 10:59 AM, luoxhu wrote:
>>> Sorry that I don't quite understand your meanning here.  I didn't grep the
>>> word "cgraph_edge_summary" in source code, do you mean add new structure
>>
>> Hello.
>>
>> He wanted to write call_summary class and so you need something similar to
>> ipa-sra.c:431. It's a data structure which associate a data to cgraph_edge.
>> Is it understandable please?
>>
>> Martin
>>
> 
> Update the patch as below with "git format-patch -U15" for review convenience,
> the GC issue is fixed after leveraging the full GC framework in ipa-profile.c, Thanks:
> 
> v6 Changes:
>   1. Define and use speculative_call_targets summary, move
>   speculative_call_target from cgraph.h to ipa-profile.c.
>   2. Use num_speculative_call_targets in cgraph_indirect_call_info.
>   3. Refine with review comments.
> 
> This patch aims to fix PR69678 caused by PGO indirect call profiling
> performance issues.
> The bug that profiling data is never working was fixed by Martin's pull
> back of topN patches, performance got GEOMEAN ~1% improvement(+24% for
> 511.povray_r specifically).
> Still, currently the default profile only generates SINGLE indirect target
> that called more than 75%.  This patch leverages MULTIPLE indirect
> targets use in LTO-WPA and LTO-LTRANS stage, as a result, function
> specialization, profiling, partial devirtualization, inlining and
> cloning could be done successfully based on it.
> Performance can get improved from 0.70 sec to 0.38 sec on simple tests.
> Details are:
>    1.  PGO with topn is enabled by default now, but only one indirect
>    target edge will be generated in ipa-profile pass, so add variables to enable
>    multiple speculative edges through passes, speculative_id will record the
>    direct edge index bind to the indirect edge, indirect_call_targets length
>    records how many direct edges owned by the indirect edge, postpone gimple_ic
>    to ipa-profile like default as inline pass will decide whether it is benefit
>    to transform indirect call.
>    2.  Use speculative_id to track and search the reference node matched
>    with the direct edge's callee for multiple targets.  Actually, it is the
>    caller's responsibility to handle the direct edges mapped to same indirect
>    edge.  speculative_call_info will return one of the direct edge specified,
>    this will leverage current IPA edge process framework mostly.
>    3.  Enable LTO WPA/LTRANS stage multiple indirect call targets analysis for
>    profile full support in ipa passes and cgraph_edge functions.  speculative_id
>    can be set by make_speculative id when multiple targets are binded to
>    one indirect edge, and cloned if new edge is cloned.  speculative_id
>    is streamed out and stream int by lto like lto_stmt_uid.
>    4.  Add 1 in module testcase and 2 cross module testcases.
>    5.  Bootstrap and regression test passed on Power8-LE.  No function
>    and performance regression for SPEC2017.
> 
> gcc/ChangeLog
> 
> 	2019-12-02  Xiong Hu Luo  <luoxhu@linux.ibm.com>
> 
> 	PR ipa/69678
> 	* Makefile.in (GTFILES): Add ipa-profile.c.
> 	* cgraph.c (symbol_table::create_edge): Init speculative_id.
> 	(cgraph_edge::make_speculative): Add param for setting speculative_id.
> 	(cgraph_edge::speculative_call_info): Update comments and find reference
> 	by speculative_id for multiple indirect targets.
> 	(cgraph_edge::resolve_speculation): Decrease the speculations
> 	for indirect edge, drop it's speculative if not direct target
> 	left. Update comments.
> 	(cgraph_edge::redirect_call_stmt_to_callee): Likewise.
> 	(cgraph_node::dump): Print num_speculative_call_targets.
> 	(cgraph_node::verify_node): Don't report error if speculative
> 	edge not include statement.
> 	(cgraph_edge::num_speculative_call_targets_p): New function.
> 	* cgraph.h (int common_target_id): Remove.
> 	(int common_target_probability): Remove.
> 	(num_speculative_call_targets): New variable.
> 	(make_speculative): Add param for setting speculative_id.
> 	(cgraph_edge::num_speculative_call_targets_p): New declare.
> 	(speculative_id): New variable.
> 	* cgraphclones.c (cgraph_node::create_clone): Clone speculative_id.
> 	* ipa-profile.c (struct speculative_call_target): New struct.
> 	(class speculative_call_summary): New class.
> 	(class speculative_call_summaries): New class.
> 	(call_sums): New variable.
> 	(ipa_profile_generate_summary): Generate indirect multiple targets summaries.
> 	(ipa_profile_write_edge_summary): New function.
> 	(ipa_profile_write_summary): Stream out indirect multiple targets summaries.
> 	(ipa_profile_dump_all_summaries): New function.
> 	(ipa_profile_read_edge_summary): New function.
> 	(ipa_profile_read_summary_section): New function.
> 	(ipa_profile_read_summary): Stream in indirect multiple targets summaries.
> 	(ipa_profile): Generate num_speculative_call_targets from
> 	summaries.
> 	* ipa-ref.h (speculative_id): New variable.
> 	* lto-cgraph.c (lto_output_edge): Remove indirect common_target_id and
> 	common_target_probability.   Stream out speculative_id and
> 	num_speculative_call_targets.
> 	(input_edge): Likewise.
> 	* predict.c (dump_prediction): Remove edges count assert to be
> 	precise.
> 	* symtab.c (symtab_node::create_reference): Init speculative_id.
> 	(symtab_node::clone_references): Clone speculative_id.
> 	(symtab_node::clone_referring): Clone speculative_id.
> 	(symtab_node::clone_reference): Clone speculative_id.
> 	(symtab_node::clear_stmts_in_references): Clear speculative_id.
> 	* tree-inline.c (copy_bb): Duplicate all the speculative edges
> 	if indirect call contains multiple speculative targets.
> 	* tree-profile.c (gimple_gen_ic_profiler): Use the new variable
> 	__gcov_indirect_call.counters and __gcov_indirect_call.callee.
> 	(gimple_gen_ic_func_profiler): Likewise.
> 	(pass_ipa_tree_profile::gate): Fix comment typos.
> 	* value-prof.h  (check_ic_target): Remove.
> 	* value-prof.c  (gimple_value_profile_transformations):
> 	Use void function gimple_ic_transform.
> 	* value-prof.c  (gimple_ic_transform): Handle topn case.
> 	Fix comment typos.  Change it to a void function.
> 
> gcc/testsuite/ChangeLog
> 
> 	2019-12-02  Xiong Hu Luo  <luoxhu@linux.ibm.com>
> 
> 	PR ipa/69678
> 	* gcc.dg/tree-prof/indir-call-prof-topn.c: New testcase.
> 	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c: New testcase.
> 	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c: New testcase.
> 	* gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c: New testcase.
> 	* lib/scandump.exp: Dump executable file name.
> 	* lib/scanwpaipa.exp: New scan-pgo-wap-ipa-dump.
> ---
>   gcc/Makefile.in                               |   1 +
>   gcc/cgraph.c                                  | 106 ++++-
>   gcc/cgraph.h                                  |  16 +-
>   gcc/cgraphclones.c                            |   1 +
>   gcc/ipa-profile.c                             | 384 ++++++++++++++++--
>   gcc/ipa-ref.h                                 |   1 +
>   gcc/lto-cgraph.c                              |  28 +-
>   gcc/predict.c                                 |   1 -
>   gcc/symtab.c                                  |   5 +
>   .../tree-prof/crossmodule-indir-call-topn-1.c |  33 ++
>   .../crossmodule-indir-call-topn-1a.c          |  22 +
>   .../tree-prof/crossmodule-indir-call-topn-2.c |  40 ++
>   .../gcc.dg/tree-prof/indir-call-prof-topn.c   |  37 ++
>   gcc/testsuite/lib/scandump.exp                |   1 +
>   gcc/testsuite/lib/scanwpaipa.exp              |  23 ++
>   gcc/tree-inline.c                             |  22 +
>   gcc/value-prof.c                              |  87 ++--
>   gcc/value-prof.h                              |   1 -
>   18 files changed, 705 insertions(+), 104 deletions(-)
>   create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
>   create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
>   create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
>   create mode 100644 gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
> 
> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> index 7d3c13230e4..366aceb8330 100644
> --- a/gcc/Makefile.in
> +++ b/gcc/Makefile.in
> @@ -2578,30 +2578,31 @@ GTFILES = $(CPPLIB_H) $(srcdir)/input.h $(srcdir)/coretypes.h \
>     $(srcdir)/omp-offload.c \
>     $(srcdir)/omp-expand.c \
>     $(srcdir)/omp-low.c \
>     $(srcdir)/targhooks.c $(out_file) $(srcdir)/passes.c $(srcdir)/cgraphunit.c \
>     $(srcdir)/cgraphclones.c \
>     $(srcdir)/tree-phinodes.c \
>     $(srcdir)/tree-ssa-alias.h \
>     $(srcdir)/tree-ssanames.h \
>     $(srcdir)/tree-vrp.h \
>     $(srcdir)/ipa-prop.h \
>     $(srcdir)/trans-mem.c \
>     $(srcdir)/lto-streamer.h \
>     $(srcdir)/target-globals.h \
>     $(srcdir)/ipa-predicate.h \
>     $(srcdir)/ipa-fnsummary.h \
> +  $(srcdir)/ipa-profile.c \
>     $(srcdir)/value-range.h \
>     $(srcdir)/vtable-verify.c \
>     $(srcdir)/asan.c \
>     $(srcdir)/ubsan.c \
>     $(srcdir)/tsan.c \
>     $(srcdir)/sanopt.c \
>     $(srcdir)/sancov.c \
>     $(srcdir)/ipa-devirt.c \
>     $(srcdir)/internal-fn.h \
>     $(srcdir)/hsa-common.c \
>     $(srcdir)/calls.c \
>     $(srcdir)/omp-general.h \
>     @all_gtfiles@
> 
>   # Compute the list of GT header files from the corresponding C sources,
> diff --git a/gcc/cgraph.c b/gcc/cgraph.c
> index b75430f3f3a..8aa4cc91939 100644
> --- a/gcc/cgraph.c
> +++ b/gcc/cgraph.c
> @@ -852,30 +852,31 @@ symbol_table::create_edge (cgraph_node *caller, cgraph_node *callee,
> 
>     edge = ggc_alloc<cgraph_edge> ();
>     edge->m_summary_id = -1;
>     edges_count++;
> 
>     gcc_assert (++edges_max_uid != 0);
>     edge->m_uid = edges_max_uid;
>     edge->aux = NULL;
>     edge->caller = caller;
>     edge->callee = callee;
>     edge->prev_caller = NULL;
>     edge->next_caller = NULL;
>     edge->prev_callee = NULL;
>     edge->next_callee = NULL;
>     edge->lto_stmt_uid = 0;
> +  edge->speculative_id = 0;
> 
>     edge->count = count;
>     edge->call_stmt = call_stmt;
>     edge->indirect_info = NULL;
>     edge->indirect_inlining_edge = 0;
>     edge->speculative = false;
>     edge->indirect_unknown_callee = indir_unknown_callee;
>     if (call_stmt && caller->call_site_hash)
>       cgraph_add_edge_to_call_site_hash (edge);
> 
>     if (cloning_p)
>       return edge;
> 
>     edge->can_throw_external
>       = call_stmt ? stmt_can_throw_external (DECL_STRUCT_FUNCTION (caller->decl),
> @@ -1041,66 +1042,95 @@ cgraph_edge::remove (void)
>      At clone materialization time, the indirect call E will
>      be expanded as:
> 
>      if (call_dest == N2)
>        n2 ();
>      else
>        call call_dest
> 
>      At this time the function just creates the direct call,
>      the reference representing the if conditional and attaches
>      them all to the original indirect call statement.
> 
>      Return direct edge created.  */
> 
>   cgraph_edge *
> -cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
> +cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count,
> +			       unsigned int speculative_id)
>   {
>     cgraph_node *n = caller;
>     ipa_ref *ref = NULL;
>     cgraph_edge *e2;
> 
>     if (dump_file)
>       fprintf (dump_file, "Indirect call -> speculative call %s => %s\n",
>   	     n->dump_name (), n2->dump_name ());
>     speculative = true;
>     e2 = n->create_edge (n2, call_stmt, direct_count);
>     initialize_inline_failed (e2);
>     e2->speculative = true;
>     if (TREE_NOTHROW (n2->decl))
>       e2->can_throw_external = false;
>     else
>       e2->can_throw_external = can_throw_external;
>     e2->lto_stmt_uid = lto_stmt_uid;
> +  e2->speculative_id = speculative_id;
>     e2->in_polymorphic_cdtor = in_polymorphic_cdtor;
>     count -= e2->count;
>     symtab->call_edge_duplication_hooks (this, e2);
>     ref = n->create_reference (n2, IPA_REF_ADDR, call_stmt);
>     ref->lto_stmt_uid = lto_stmt_uid;
> +  ref->speculative_id = speculative_id;
>     ref->speculative = speculative;
>     n2->mark_address_taken ();
>     return e2;
>   }
> 
> -/* Speculative call consist of three components:
> -   1) an indirect edge representing the original call
> -   2) an direct edge representing the new call
> -   3) ADDR_EXPR reference representing the speculative check.
> -   All three components are attached to single statement (the indirect
> -   call) and if one of them exists, all of them must exist.
> +/* Speculative calls represent a transformation of indirect calls
> +   which may be later inserted into gimple in the following form:
> 
> -   Given speculative call edge, return all three components.
> +   if (call_dest == target1)
> +   target1 ();
> +   else if (call_dest == target2)
> +   target2 ();
> +   else
> +   call_dest ();
> +
> +   This is a win in case when target1 and target2 are common values for
> +   call_dest as determined by ipa-devirt or indirect call profiling.
> +   In particular this may enable inlining and other optimizations.
> +
> +   Speculative call consists of the following main components:
> +
> +   1) One or more "speculative" direct call (num_speculative_call_targets is
> +   speculative direct call count belongs to the speculative indirect call)
> +   2) One or more IPA_REF_ADDR references (representing the fact that code above
> +   takes address of target1 and target2)
> +   3) The fallback "speculative" indirect call
> +
> +   Direct calls and corresponding references are linked by
> +   speculative_id.
> +
> +   speculative_call_info returns triple
> +   (direct_call, indirect call, IPA_REF_ADDR reference)
> +   when called on one edge participating in the speculative call:
> +
> +   1) If called on direct call, its corresponding IPA_REF_ADDR and related
> +   indirect call are returned.
> +
> +   2) If called on indirect call, it will return one of direct edges and its
> +   matching IPA_REF_ADDR.
>    */
> 
>   void
>   cgraph_edge::speculative_call_info (cgraph_edge *&direct,
>   				    cgraph_edge *&indirect,
>   				    ipa_ref *&reference)
>   {
>     ipa_ref *ref;
>     int i;
>     cgraph_edge *e2;
>     cgraph_edge *e = this;
> 
>     if (!e->indirect_unknown_callee)
>       for (e2 = e->caller->indirect_calls;
>   	 e2->call_stmt != e->call_stmt || e2->lto_stmt_uid != e->lto_stmt_uid;
> @@ -1116,47 +1146,56 @@ cgraph_edge::speculative_call_info (cgraph_edge *&direct,
>   	  gcc_assert (e->speculative && !e->indirect_unknown_callee);
>   	}
>         else
>   	for (e = e->caller->callees;
>   	     e2->call_stmt != e->call_stmt
>   	     || e2->lto_stmt_uid != e->lto_stmt_uid;
>   	     e = e->next_callee)
>   	  ;
>       }
>     gcc_assert (e->speculative && e2->speculative);
>     direct = e;
>     indirect = e2;
> 
>     reference = NULL;
>     for (i = 0; e->caller->iterate_reference (i, ref); i++)
> -    if (ref->speculative
> +    if (ref->speculative && ref->speculative_id == e->speculative_id
>   	&& ((ref->stmt && ref->stmt == e->call_stmt)
>   	    || (!ref->stmt && ref->lto_stmt_uid == e->lto_stmt_uid)))
>         {
>   	reference = ref;
>   	break;
>         }
> 
>     /* Speculative edge always consist of all three components - direct edge,
>        indirect and reference.  */
>     
>     gcc_assert (e && e2 && ref);
>   }
> 
>   /* Speculative call edge turned out to be direct call to CALLEE_DECL.
>      Remove the speculative call sequence and return edge representing the call.
> -   It is up to caller to redirect the call as appropriate. */
> +
> +   For "speculative" indirect call that contains multiple "speculative"
> +   targets (i.e. edge->indirect_info->num_speculative_call_targets > 1),
> +   decrease the count and only remove current direct edge.
> +
> +   If no speculative direct call left to the speculative indirect call, remove
> +   the speculative of both the indirect call and corresponding direct edge.
> +
> +   It is up to caller to iteratively resolve each "speculative" direct call and
> +   redirect the call as appropriate.  */
> 
>   cgraph_edge *
>   cgraph_edge::resolve_speculation (tree callee_decl)
>   {
>     cgraph_edge *edge = this;
>     cgraph_edge *e2;
>     ipa_ref *ref;
> 
>     gcc_assert (edge->speculative);
>     edge->speculative_call_info (e2, edge, ref);
>     if (!callee_decl
>         || !ref->referred->semantically_equivalent_p
>   	   (symtab_node::get (callee_decl)))
>       {
>         if (dump_file)
> @@ -1177,31 +1216,40 @@ cgraph_edge::resolve_speculation (tree callee_decl)
>   		       e2->callee->dump_name ());
>   	    }
>   	}
>       }
>     else
>       {
>         cgraph_edge *tmp = edge;
>         if (dump_file)
>           fprintf (dump_file, "Speculative call turned into direct call.\n");
>         edge = e2;
>         e2 = tmp;
>         /* FIXME:  If EDGE is inlined, we should scale up the frequencies and counts
>            in the functions inlined through it.  */
>       }
>     edge->count += e2->count;
> -  edge->speculative = false;
> +  if (edge->num_speculative_call_targets_p ())
> +    {
> +      /* The indirect edge has multiple speculative targets, don't remove
> +	 speculative until all related direct edges are resolved.  */
> +      edge->indirect_info->num_speculative_call_targets--;
> +      if (!edge->indirect_info->num_speculative_call_targets)
> +	edge->speculative = false;
> +    }
> +  else
> +    edge->speculative = false;
>     e2->speculative = false;
>     ref->remove_reference ();
>     if (e2->indirect_unknown_callee || e2->inline_failed)
>       e2->remove ();
>     else
>       e2->callee->remove_symbol_and_inline_clones ();
>     if (edge->caller->call_site_hash)
>       cgraph_update_edge_in_call_site_hash (edge);
>     return edge;
>   }
> 
>   /* Make an indirect edge with an unknown callee an ordinary edge leading to
>      CALLEE.  DELTA is an integer constant that is to be added to the this
>      pointer (first parameter) to compensate for skipping a thunk adjustment.  */
> 
> @@ -1237,31 +1285,41 @@ cgraph_edge::make_direct (cgraph_node *callee)
>     prev_callee = NULL;
>     next_callee = caller->callees;
>     if (caller->callees)
>       caller->callees->prev_callee = edge;
>     caller->callees = edge;
> 
>     /* Insert to callers list of the new callee.  */
>     edge->set_callee (callee);
> 
>     /* We need to re-determine the inlining status of the edge.  */
>     initialize_inline_failed (edge);
>     return edge;
>   }
> 
>   /* If necessary, change the function declaration in the call statement
> -   associated with E so that it corresponds to the edge callee.  */
> +   associated with E so that it corresponds to the edge callee.
> +
> +   The edge could be one of speculative direct call generated from speculative
> +   indirect call.  In this circumstance, decrease the speculative targets
> +   count (i.e. num_speculative_call_targets) and redirect call stmt to the
> +   corresponding i-th target.  If no speculative direct call left to the
> +   speculative indirect call, remove "speculative" of the indirect call and
> +   also redirect stmt to it's final direct target.
> +
> +   It is up to caller to iteratively transform each "speculative"
> +   direct call as appropriate.  */
> 
>   gimple *
>   cgraph_edge::redirect_call_stmt_to_callee (void)
>   {
>     cgraph_edge *e = this;
> 
>     tree decl = gimple_call_fndecl (e->call_stmt);
>     gcall *new_stmt;
>     gimple_stmt_iterator gsi;
> 
>     if (e->speculative)
>       {
>         cgraph_edge *e2;
>         gcall *new_stmt;
>         ipa_ref *ref;
> @@ -1285,31 +1343,41 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
>   	    }
>   	  gcc_assert (e2->speculative);
>   	  push_cfun (DECL_STRUCT_FUNCTION (e->caller->decl));
> 
>   	  profile_probability prob = e->count.probability_in (e->count
>   							      + e2->count);
>   	  if (!prob.initialized_p ())
>   	    prob = profile_probability::even ();
>   	  new_stmt = gimple_ic (e->call_stmt,
>   				dyn_cast<cgraph_node *> (ref->referred),
>   				prob);
>   	  e->speculative = false;
>   	  e->caller->set_call_stmt_including_clones (e->call_stmt, new_stmt,
>   						     false);
>   	  e->count = gimple_bb (e->call_stmt)->count;
> -	  e2->speculative = false;
> +	  if (e2->num_speculative_call_targets_p ())
> +	    {
> +	      /* The indirect edge has multiple speculative targets, don't
> +		 remove speculative until all related direct edges are
> +		 redirected.  */
> +	      e2->indirect_info->num_speculative_call_targets--;
> +	      if (!e2->indirect_info->num_speculative_call_targets)
> +		e2->speculative = false;
> +	    }
> +	  else
> +	    e2->speculative = false;
>   	  e2->count = gimple_bb (e2->call_stmt)->count;
>   	  ref->speculative = false;
>   	  ref->stmt = NULL;
>   	  /* Indirect edges are not both in the call site hash.
>   	     get it updated.  */
>   	  if (e->caller->call_site_hash)
>   	    cgraph_update_edge_in_call_site_hash (e2);
>   	  pop_cfun ();
>   	  /* Continue redirecting E to proper target.  */
>   	}
>       }
> 
> 
>     if (e->indirect_unknown_callee
>         || decl == e->callee->decl)
> @@ -2097,30 +2165,32 @@ cgraph_node::dump (FILE *f)
>   	}
>         else
>           fprintf (f, "   Indirect call");
>         edge->dump_edge_flags (f);
>         if (edge->indirect_info->param_index != -1)
>   	{
>   	  fprintf (f, " of param:%i", edge->indirect_info->param_index);
>   	  if (edge->indirect_info->agg_contents)
>   	   fprintf (f, " loaded from %s %s at offset %i",
>   		    edge->indirect_info->member_ptr ? "member ptr" : "aggregate",
>   		    edge->indirect_info->by_ref ? "passed by reference":"",
>   		    (int)edge->indirect_info->offset);
>   	  if (edge->indirect_info->vptr_changed)
>   	    fprintf (f, " (vptr maybe changed)");
>   	}
> +      fprintf (f, " Num speculative call targets: %i",
> +	       edge->indirect_info->num_speculative_call_targets);
>         fprintf (f, "\n");
>         if (edge->indirect_info->polymorphic)
>   	edge->indirect_info->context.dump (f);
>       }
>   }
> 
>   /* Dump call graph node to file F in graphviz format.  */
> 
>   void
>   cgraph_node::dump_graphviz (FILE *f)
>   {
>     cgraph_edge *edge;
> 
>     for (edge = callees; edge; edge = edge->next_callee)
>       {
> @@ -3374,31 +3444,31 @@ cgraph_node::verify_node (void)
>   	      }
>   	    for (i = 0; iterate_reference (i, ref); i++)
>   	      if (ref->stmt && !stmts.contains (ref->stmt))
>   		{
>   		  error ("reference to dead statement");
>   		  cgraph_debug_gimple_stmt (this_cfun, ref->stmt);
>   		  error_found = true;
>   		}
>   	}
>         else
>   	/* No CFG available?!  */
>   	gcc_unreachable ();
> 
>         for (e = callees; e; e = e->next_callee)
>   	{
> -	  if (!e->aux)
> +	  if (!e->aux && !e->speculative)
>   	    {
>   	      error ("edge %s->%s has no corresponding call_stmt",
>   		     identifier_to_locale (e->caller->name ()),
>   		     identifier_to_locale (e->callee->name ()));
>   	      cgraph_debug_gimple_stmt (this_cfun, e->call_stmt);
>   	      error_found = true;
>   	    }
>   	  e->aux = 0;
>   	}
>         for (e = indirect_calls; e; e = e->next_callee)
>   	{
>   	  if (!e->aux && !e->speculative)
>   	    {
>   	      error ("an indirect edge from %s has no corresponding call_stmt",
>   		     identifier_to_locale (e->caller->name ()));
> @@ -3711,30 +3781,38 @@ cgraph_edge::possibly_call_in_translation_unit_p (void)
>     if (!TREE_PUBLIC (callee->decl) && !DECL_EXTERNAL (callee->decl))
>       return true;
> 
>     /* Otherwise we need to lookup prevailing symbol (symbol table is not merged,
>        yet) and see if it is a definition.  In fact we may also resolve aliases,
>        but that is probably not too important.  */
>     symtab_node *node = callee;
>     for (int n = 10; node->previous_sharing_asm_name && n ; n--)
>       node = node->previous_sharing_asm_name;
>     if (node->previous_sharing_asm_name)
>       node = symtab_node::get_for_asmname (DECL_ASSEMBLER_NAME (callee->decl));
>     gcc_assert (TREE_PUBLIC (node->decl));
>     return node->get_availability () >= AVAIL_INTERPOSABLE;
>   }
> 
> +/* Return num_speculative_targets of this edge.  */
> +
> +int
> +cgraph_edge::num_speculative_call_targets_p (void)
> +{
> +  return indirect_info ? indirect_info->num_speculative_call_targets : 0;
> +}
> +
>   /* A stashed copy of "symtab" for use by selftest::symbol_table_test.
>      This needs to be a global so that it can be a GC root, and thus
>      prevent the stashed copy from being garbage-collected if the GC runs
>      during a symbol_table_test.  */
> 
>   symbol_table *saved_symtab;
> 
>   #if CHECKING_P
> 
>   namespace selftest {
> 
>   /* class selftest::symbol_table_test.  */
> 
>   /* Constructor.  Store the old value of symtab, and create a new one.  */
> 
> diff --git a/gcc/cgraph.h b/gcc/cgraph.h
> index 9c086fedaef..4cb047776e9 100644
> --- a/gcc/cgraph.h
> +++ b/gcc/cgraph.h
> @@ -1652,34 +1652,33 @@ class GTY(()) cgraph_indirect_call_info
>   {
>   public:
>     /* When agg_content is set, an offset where the call pointer is located
>        within the aggregate.  */
>     HOST_WIDE_INT offset;
>     /* Context of the polymorphic call; use only when POLYMORPHIC flag is set.  */
>     ipa_polymorphic_call_context context;
>     /* OBJ_TYPE_REF_TOKEN of a polymorphic call (if polymorphic is set).  */
>     HOST_WIDE_INT otr_token;
>     /* Type of the object from OBJ_TYPE_REF_OBJECT. */
>     tree otr_type;
>     /* Index of the parameter that is called.  */
>     int param_index;
>     /* ECF flags determined from the caller.  */
>     int ecf_flags;
> -  /* Profile_id of common target obtained from profile.  */
> -  int common_target_id;
> -  /* Probability that call will land in function with COMMON_TARGET_ID.  */
> -  int common_target_probability;
> +
> +  /* Number of speculative call targets, it's less than GCOV_TOPN_VALUES.  */
> +  unsigned num_speculative_call_targets : 16;
> 
>     /* Set when the call is a virtual call with the parameter being the
>        associated object pointer rather than a simple direct call.  */
>     unsigned polymorphic : 1;
>     /* Set when the call is a call of a pointer loaded from contents of an
>        aggregate at offset.  */
>     unsigned agg_contents : 1;
>     /* Set when this is a call through a member pointer.  */
>     unsigned member_ptr : 1;
>     /* When the agg_contents bit is set, this one determines whether the
>        destination is loaded from a parameter passed by reference. */
>     unsigned by_ref : 1;
>     /* When the agg_contents bit is set, this one determines whether we can
>        deduce from the function body that the loaded value from the reference is
>        never modified between the invocation of the function and the load
> @@ -1712,31 +1711,32 @@ public:
>     /* If the edge does not lead to a thunk, simply redirect it to N.  Otherwise
>        create one or more equivalent thunks for N and redirect E to the first in
>        the chain.  Note that it is then necessary to call
>        n->expand_all_artificial_thunks once all callers are redirected.  */
>     void redirect_callee_duplicating_thunks (cgraph_node *n);
> 
>     /* Make an indirect edge with an unknown callee an ordinary edge leading to
>        CALLEE.  DELTA is an integer constant that is to be added to the this
>        pointer (first parameter) to compensate for skipping
>        a thunk adjustment.  */
>     cgraph_edge *make_direct (cgraph_node *callee);
> 
>     /* Turn edge into speculative call calling N2. Update
>        the profile so the direct call is taken COUNT times
>        with FREQUENCY.  */
> -  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count);
> +  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count,
> +				 unsigned int speculative_id = 0);
> 
>      /* Given speculative call edge, return all three components.  */
>     void speculative_call_info (cgraph_edge *&direct, cgraph_edge *&indirect,
>   			      ipa_ref *&reference);
> 
>     /* Speculative call edge turned out to be direct call to CALLEE_DECL.
>        Remove the speculative call sequence and return edge representing the call.
>        It is up to caller to redirect the call as appropriate. */
>     cgraph_edge *resolve_speculation (tree callee_decl = NULL);
> 
>     /* If necessary, change the function declaration in the call statement
>        associated with the edge so that it corresponds to the edge callee.  */
>     gimple *redirect_call_stmt_to_callee (void);
> 
>     /* Create clone of edge in the node N represented
> @@ -1771,49 +1771,55 @@ public:
>       return m_summary_id;
>     }
> 
>     /* Rebuild cgraph edges for current function node.  This needs to be run after
>        passes that don't update the cgraph.  */
>     static unsigned int rebuild_edges (void);
> 
>     /* Rebuild cgraph references for current function node.  This needs to be run
>        after passes that don't update the cgraph.  */
>     static void rebuild_references (void);
> 
>     /* During LTO stream in this can be used to check whether call can possibly
>        be internal to the current translation unit.  */
>     bool possibly_call_in_translation_unit_p (void);
> 
> +  /* Return num_speculative_targets of this edge.  */
> +  int num_speculative_call_targets_p (void);
> +
>     /* Expected number of executions: calculated in profile.c.  */
>     profile_count count;
>     cgraph_node *caller;
>     cgraph_node *callee;
>     cgraph_edge *prev_caller;
>     cgraph_edge *next_caller;
>     cgraph_edge *prev_callee;
>     cgraph_edge *next_callee;
>     gcall *call_stmt;
>     /* Additional information about an indirect call.  Not cleared when an edge
>        becomes direct.  */
>     cgraph_indirect_call_info *indirect_info;
>     PTR GTY ((skip (""))) aux;
>     /* When equal to CIF_OK, inline this call.  Otherwise, points to the
>        explanation why function was not inlined.  */
>     enum cgraph_inline_failed_t inline_failed;
>     /* The stmt_uid of call_stmt.  This is used by LTO to recover the call_stmt
>        when the function is serialized in.  */
>     unsigned int lto_stmt_uid;
> +  /* speculative id is used to link direct calls with their corresponding
> +     IPA_REF_ADDR references when representing speculative calls.  */
> +  unsigned int speculative_id : 16;
>     /* Whether this edge was made direct by indirect inlining.  */
>     unsigned int indirect_inlining_edge : 1;
>     /* Whether this edge describes an indirect call with an undetermined
>        callee.  */
>     unsigned int indirect_unknown_callee : 1;
>     /* Whether this edge is still a dangling  */
>     /* True if the corresponding CALL stmt cannot be inlined.  */
>     unsigned int call_stmt_cannot_inline_p : 1;
>     /* Can this call throw externally?  */
>     unsigned int can_throw_external : 1;
>     /* Edges with SPECULATIVE flag represents indirect calls that was
>        speculatively turned into direct (i.e. by profile feedback).
>        The final code sequence will have form:
> 
>        if (call_target == expected_fn)
> diff --git a/gcc/cgraphclones.c b/gcc/cgraphclones.c
> index 81c5dfd194f..da9a4f3f6f5 100644
> --- a/gcc/cgraphclones.c
> +++ b/gcc/cgraphclones.c
> @@ -121,30 +121,31 @@ cgraph_edge::clone (cgraph_node *n, gcall *call_stmt, unsigned stmt_uid,
>       }
>     else
>       {
>         new_edge = n->create_edge (callee, call_stmt, prof_count, true);
>         if (indirect_info)
>   	{
>   	  new_edge->indirect_info
>   	    = ggc_cleared_alloc<cgraph_indirect_call_info> ();
>   	  *new_edge->indirect_info = *indirect_info;
>   	}
>       }
> 
>     new_edge->inline_failed = inline_failed;
>     new_edge->indirect_inlining_edge = indirect_inlining_edge;
>     new_edge->lto_stmt_uid = stmt_uid;
> +  new_edge->speculative_id = speculative_id;
>     /* Clone flags that depend on call_stmt availability manually.  */
>     new_edge->can_throw_external = can_throw_external;
>     new_edge->call_stmt_cannot_inline_p = call_stmt_cannot_inline_p;
>     new_edge->speculative = speculative;
>     new_edge->in_polymorphic_cdtor = in_polymorphic_cdtor;
> 
>     /* Update IPA profile.  Local profiles need no updating in original.  */
>     if (update_original)
>       count = count.combine_with_ipa_count_within (count.ipa ()
>   						 - new_edge->count.ipa (),
>   						 caller->count);
>     symtab->call_edge_duplication_hooks (this, new_edge);
>     return new_edge;
>   }
> 
> diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
> index 4b28b94aaad..17ab5ce60d7 100644
> --- a/gcc/ipa-profile.c
> +++ b/gcc/ipa-profile.c
> @@ -147,144 +147,417 @@ dump_histogram (FILE *file, vec<histogram_entry *> histogram)
>     if (!overall_size)
>       overall_size = 1;
>     for (i = 0; i < histogram.length (); i++)
>       {
>         cumulated_time += histogram[i]->count * histogram[i]->time;
>         cumulated_size += histogram[i]->size;
>         fprintf (file, "  %" PRId64": time:%i (%2.2f) size:%i (%2.2f)\n",
>   	       (int64_t) histogram[i]->count,
>   	       histogram[i]->time,
>   	       cumulated_time * 100.0 / overall_time,
>   	       histogram[i]->size,
>   	       cumulated_size * 100.0 / overall_size);
>      }
>   }
> 
> -/* Collect histogram from CFG profiles.  */
> +/* Structure containing speculative target information from profile.  */
> +
> +struct GTY (()) speculative_call_target
> +{
> +  speculative_call_target (unsigned int id, int prob)
> +    : target_id (id), target_probability (prob)
> +  {
> +  }
> +
> +  /* Profile_id of target obtained from profile.  */
> +  unsigned int target_id;
> +  /* Probability that call will land in function with target_id.  */
> +  int target_probability;
> +};
> +
> +class GTY ((for_user)) speculative_call_summary
> +{
> +public:
> +  speculative_call_summary () : speculative_call_targets ()
> +  {}
> +
> +  vec<speculative_call_target, va_gc> *speculative_call_targets;
> +  ~speculative_call_summary ();
> +
> +  void dump (FILE *f);
> +
> +  /* Check whether this is a empty summary.  */
> +  bool is_empty ();
> +};
> +
> +  /* Class to manage call summaries.  */
> +
> +class GTY ((user)) ipa_profile_call_summaries
> +  : public call_summary<speculative_call_summary *>
> +{
> +public:
> +  ipa_profile_call_summaries (symbol_table *table, bool ggc)
> +    : call_summary<speculative_call_summary *> (table, ggc)
> +  {}
> +
> +  /* Duplicate info when an edge is cloned.  */
> +  virtual void duplicate (cgraph_edge *, cgraph_edge *,
> +			  speculative_call_summary *old_sum,
> +			  speculative_call_summary *new_sum);
> +};
> +
> +static GTY (()) ipa_profile_call_summaries *call_sums = NULL;
> +
> +speculative_call_summary::~speculative_call_summary ()
> +{
> +  if (speculative_call_targets)
> +    {
> +      vec_free (speculative_call_targets);
> +      speculative_call_targets = NULL;
> +    }
> +}
> +
> +/* Dump all information in speculative call summary to F.  */
> +
> +void
> +speculative_call_summary::dump (FILE *f)
> +{
> +  speculative_call_target *item;
> +  cgraph_node *n2;
> +  unsigned int i;
> +
> +  FOR_EACH_VEC_SAFE_ELT (speculative_call_targets, i, item)
> +    {
> +      n2 = find_func_by_profile_id (item->target_id);
> +      if (n2)
> +	fprintf (f, "    The %i speculative target is %s with prob %3.2f\n", i,
> +		 n2->dump_name (),
> +		 item->target_probability / (float) REG_BR_PROB_BASE);
> +      else
> +	fprintf (f, "    The %i speculative target is %u with prob %3.2f\n", i,
> +		 item->target_id,
> +		 item->target_probability / (float) REG_BR_PROB_BASE);
> +    }
> +}
> +
> +/* Check whether this is a empty summary.  */
> +bool
> +speculative_call_summary::is_empty ()
> +{
> +  return speculative_call_targets == NULL
> +	 || speculative_call_targets->is_empty ();
> +}
> +
> +/* Duplicate info when an edge is cloned.  */
> +
> +void
> +ipa_profile_call_summaries::duplicate (cgraph_edge *, cgraph_edge *,
> +				  speculative_call_summary *old_sum,
> +				  speculative_call_summary *new_sum)
> +{
> +  if (!old_sum || !old_sum->speculative_call_targets)
> +    return;
> +
> +  speculative_call_target *item;
> +  unsigned int i;
> +
> +  FOR_EACH_VEC_SAFE_ELT (old_sum->speculative_call_targets, i, item)
> +    {
> +      vec_safe_push (new_sum->speculative_call_targets, *item);
> +    }
> +}
> +
> +/* Collect histogram and speculative target summaries from CFG profiles.  */
> 
>   static void
>   ipa_profile_generate_summary (void)
>   {
>     struct cgraph_node *node;
>     gimple_stmt_iterator gsi;
>     basic_block bb;
> 
>     hash_table<histogram_hash> hashtable (10);
> -
> +
> +  gcc_checking_assert (!call_sums);
> +  call_sums = (new (ggc_alloc_no_dtor<ipa_profile_call_summaries> ())
> +		 ipa_profile_call_summaries (symtab, true));
> +
>     FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
>       if (ENTRY_BLOCK_PTR_FOR_FN (DECL_STRUCT_FUNCTION (node->decl))->count.ipa_p ())
>         FOR_EACH_BB_FN (bb, DECL_STRUCT_FUNCTION (node->decl))
>   	{
>   	  int time = 0;
>   	  int size = 0;
>   	  for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
>   	    {
>   	      gimple *stmt = gsi_stmt (gsi);
>   	      if (gimple_code (stmt) == GIMPLE_CALL
>   		  && !gimple_call_fndecl (stmt))
>   		{
>   		  histogram_value h;
>   		  h = gimple_histogram_value_of_type
>   			(DECL_STRUCT_FUNCTION (node->decl),
>   			 stmt, HIST_TYPE_INDIR_CALL);
>   		  /* No need to do sanity check: gimple_ic_transform already
>   		     takes away bad histograms.  */
>   		  if (h)
>   		    {
>   		      gcov_type val, count, all;
> -		      if (get_nth_most_common_value (NULL, "indirect call", h,
> -						     &val, &count, &all))
> +		      struct cgraph_edge *e = node->get_edge (stmt);
> +		      if (e && !e->indirect_unknown_callee)
> +			continue;
> +
> +		      speculative_call_summary *csum
> +			= call_sums->get_create (e);
> +
> +		      for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
>   			{
> -			  struct cgraph_edge * e = node->get_edge (stmt);
> -			  if (e && !e->indirect_unknown_callee)
> +			  if (!get_nth_most_common_value (NULL, "indirect call",
> +							  h, &val, &count, &all,
> +							  j))
>   			    continue;
> 
> -			  e->indirect_info->common_target_id = val;
> -			  e->indirect_info->common_target_probability
> -			    = GCOV_COMPUTE_SCALE (count, all);
> -			  if (e->indirect_info->common_target_probability > REG_BR_PROB_BASE)
> +			  if (val == 0)
> +			    continue;
> +
> +			  speculative_call_target item (
> +			    val, GCOV_COMPUTE_SCALE (count, all));
> +			  if (item.target_probability > REG_BR_PROB_BASE)
>   			    {
>   			      if (dump_file)
> -				fprintf (dump_file, "Probability capped to 1\n");
> -			      e->indirect_info->common_target_probability = REG_BR_PROB_BASE;
> +				fprintf (dump_file,
> +					 "Probability capped to 1\n");
> +			      item.target_probability = REG_BR_PROB_BASE;
>   			    }
> +			  vec_safe_push (csum->speculative_call_targets, item);
>   			}
> +
>   		      gimple_remove_histogram_value (DECL_STRUCT_FUNCTION (node->decl),
>   						      stmt, h);
>   		    }
>   		}
>   	      time += estimate_num_insns (stmt, &eni_time_weights);
>   	      size += estimate_num_insns (stmt, &eni_size_weights);
>   	    }
>   	  if (bb->count.ipa_p () && bb->count.initialized_p ())
>   	    account_time_size (&hashtable, histogram, bb->count.ipa ().to_gcov_type (),
>   			       time, size);
>   	}
>     histogram.qsort (cmp_counts);
>   }
> 
> +/* Serialize the speculative summary info for LTO.  */
> +
> +static void
> +ipa_profile_write_edge_summary (lto_simple_output_block *ob,
> +				speculative_call_summary *csum)
> +{
> +  speculative_call_target *item;
> +  unsigned int i;
> +  unsigned len = 0;
> +
> +  if (!csum->is_empty ())
> +    len = csum->speculative_call_targets->length ();
> +
> +  gcc_assert (len <= GCOV_TOPN_VALUES);
> +
> +  streamer_write_hwi_stream (ob->main_stream, len);
> +
> +  if (len)
> +    {
> +      FOR_EACH_VEC_SAFE_ELT (csum->speculative_call_targets, i, item)
> +	{
> +	  gcc_assert (item->target_id);
> +	  streamer_write_hwi_stream (ob->main_stream, item->target_id);
> +	  streamer_write_hwi_stream (ob->main_stream, item->target_probability);
> +	}
> +    }
> +}
> +
>   /* Serialize the ipa info for lto.  */
> 
>   static void
>   ipa_profile_write_summary (void)
>   {
>     struct lto_simple_output_block *ob
>       = lto_create_simple_output_block (LTO_section_ipa_profile);
>     unsigned int i;
> 
>     streamer_write_uhwi_stream (ob->main_stream, histogram.length ());
>     for (i = 0; i < histogram.length (); i++)
>       {
>         streamer_write_gcov_count_stream (ob->main_stream, histogram[i]->count);
>         streamer_write_uhwi_stream (ob->main_stream, histogram[i]->time);
>         streamer_write_uhwi_stream (ob->main_stream, histogram[i]->size);
>       }
> +
> +  if (!call_sums)
> +    return;
> +
> +  /* Serialize speculative targets information.  */
> +  unsigned int count = 0;
> +  lto_symtab_encoder_t encoder = ob->decl_state->symtab_node_encoder;
> +  lto_symtab_encoder_iterator lsei;
> +  cgraph_node *node;
> +
> +  for (lsei = lsei_start_function_in_partition (encoder); !lsei_end_p (lsei);
> +       lsei_next_function_in_partition (&lsei))
> +    {
> +      node = lsei_cgraph_node (lsei);
> +      if (node->definition && node->has_gimple_body_p ()
> +	  && node->indirect_calls)
> +	count++;
> +    }
> +
> +  streamer_write_uhwi_stream (ob->main_stream, count);
> +
> +  /* Process all of the functions.  */
> +  for (lsei = lsei_start_function_in_partition (encoder);
> +       !lsei_end_p (lsei) && count; lsei_next_function_in_partition (&lsei))
> +    {
> +      cgraph_node *node = lsei_cgraph_node (lsei);
> +      if (node->definition && node->has_gimple_body_p ()
> +	  && node->indirect_calls)
> +	{
> +	  int node_ref = lto_symtab_encoder_encode (encoder, node);
> +	  streamer_write_uhwi_stream (ob->main_stream, node_ref);
> +
> +	  for (cgraph_edge *e = node->indirect_calls; e; e = e->next_callee)
> +	    {
> +	      speculative_call_summary *csum = call_sums->get_create (e);
> +	      ipa_profile_write_edge_summary (ob, csum);
> +	    }
> +      }
> +    }
> +
>     lto_destroy_simple_output_block (ob);
>   }
> 
> -/* Deserialize the ipa info for lto.  */
> +/* Dump all profile summary data for all cgraph nodes and edges to file F.  */
> +
> +static void
> +ipa_profile_dump_all_summaries (FILE *f)
> +{
> +  fprintf (dump_file,
> +	   "\n========== IPA-profile speculative targets: ==========\n");
> +  cgraph_node *node;
> +  FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
> +    {
> +      fprintf (f, "\nSummary for node %s:\n", node->dump_name ());
> +      for (cgraph_edge *e = node->indirect_calls; e; e = e->next_callee)
> +	{
> +	  fprintf (f, "  Summary for %s of indirect edge %d:\n",
> +		   e->caller->dump_name (), e->lto_stmt_uid);
> +	  speculative_call_summary *csum = call_sums->get_create (e);
> +	  if (!csum->is_empty ())
> +	    csum->dump (f);
> +	  else
> +	    fprintf (f, "    No indirect call summary.\n");
> +	}
> +    }
> +  fprintf (f, "\n\n");
> +}
> +
> +/* Read speculative targets information about edge for LTO WPA.  */
> +
> +static void
> +ipa_profile_read_edge_summary (class lto_input_block *ib, cgraph_edge *edge)
> +{
> +  unsigned i, len;
> +
> +  len = streamer_read_hwi (ib);
> +  gcc_assert (len <= GCOV_TOPN_VALUES);
> +
> +  speculative_call_summary *csum = call_sums->get_create (edge);
> +
> +  for (i = 0; i < len; i++)
> +  {
> +    speculative_call_target item (streamer_read_hwi (ib),
> +	streamer_read_hwi (ib));
> +    vec_safe_push (csum->speculative_call_targets, item);
> +  }
> +}
> +
> +/* Read profile speculative targets section information for LTO WPA.  */
> +
> +static void
> +ipa_profile_read_summary_section (struct lto_file_decl_data *file_data,
> +				  class lto_input_block *ib)
> +{
> +  if (!ib)
> +    return;
> +
> +  lto_symtab_encoder_t encoder = file_data->symtab_node_encoder;
> +
> +  unsigned int count = streamer_read_uhwi (ib);
> +
> +  unsigned int i;
> +  unsigned int index;
> +  cgraph_node * node;
> +
> +  for (i = 0; i < count; i++)
> +    {
> +      index = streamer_read_uhwi (ib);
> +      encoder = file_data->symtab_node_encoder;
> +      node
> +	= dyn_cast<cgraph_node *> (lto_symtab_encoder_deref (encoder, index));
> +
> +      for (cgraph_edge *e = node->indirect_calls; e; e = e->next_callee)
> +	ipa_profile_read_edge_summary (ib, e);
> +    }
> +}
> +
> +/* Deserialize the IPA histogram and speculative targets summary info for LTO.
> +   */
> 
>   static void
>   ipa_profile_read_summary (void)
>   {
>     struct lto_file_decl_data ** file_data_vec
>       = lto_get_file_decl_data ();
>     struct lto_file_decl_data * file_data;
>     int j = 0;
> 
>     hash_table<histogram_hash> hashtable (10);
> 
> +  gcc_checking_assert (!call_sums);
> +  call_sums = (new (ggc_alloc_no_dtor<ipa_profile_call_summaries> ())
> +		 ipa_profile_call_summaries (symtab, true));
> +
>     while ((file_data = file_data_vec[j++]))
>       {
>         const char *data;
>         size_t len;
>         class lto_input_block *ib
>   	= lto_create_simple_input_block (file_data,
>   					 LTO_section_ipa_profile,
>   					 &data, &len);
>         if (ib)
>   	{
>             unsigned int num = streamer_read_uhwi (ib);
>   	  unsigned int n;
>   	  for (n = 0; n < num; n++)
>   	    {
>   	      gcov_type count = streamer_read_gcov_count (ib);
>   	      int time = streamer_read_uhwi (ib);
>   	      int size = streamer_read_uhwi (ib);
>   	      account_time_size (&hashtable, histogram,
>   				 count, time, size);
>   	    }
> +
> +	  ipa_profile_read_summary_section (file_data, ib);
> +
>   	  lto_destroy_simple_input_block (file_data,
>   					  LTO_section_ipa_profile,
>   					  ib, data, len);
>   	}
>       }
>     histogram.qsort (cmp_counts);
>   }
> 
>   /* Data used by ipa_propagate_frequency.  */
> 
>   struct ipa_propagate_frequency_data
>   {
>     cgraph_node *function_symbol;
>     bool maybe_unlikely_executed;
>     bool maybe_executed_once;
> @@ -500,46 +773,45 @@ check_argument_count (struct cgraph_node *n, struct cgraph_edge *e)
>   /* Simple ipa profile pass propagating frequencies across the callgraph.  */
> 
>   static unsigned int
>   ipa_profile (void)
>   {
>     struct cgraph_node **order;
>     struct cgraph_edge *e;
>     int order_pos;
>     bool something_changed = false;
>     int i;
>     gcov_type overall_time = 0, cutoff = 0, cumulated = 0, overall_size = 0;
>     struct cgraph_node *n,*n2;
>     int nindirect = 0, ncommon = 0, nunknown = 0, nuseless = 0, nconverted = 0;
>     int nmismatch = 0, nimpossible = 0;
>     bool node_map_initialized = false;
> +  gcov_type threshold;
> 
>     if (dump_file)
>       dump_histogram (dump_file, histogram);
>     for (i = 0; i < (int)histogram.length (); i++)
>       {
>         overall_time += histogram[i]->count * histogram[i]->time;
>         overall_size += histogram[i]->size;
>       }
> +  threshold = 0;
>     if (overall_time)
>       {
> -      gcov_type threshold;
> -
>         gcc_assert (overall_size);
> 
>         cutoff = (overall_time * param_hot_bb_count_ws_permille + 500) / 1000;
> -      threshold = 0;
>         for (i = 0; cumulated < cutoff; i++)
>   	{
>   	  cumulated += histogram[i]->count * histogram[i]->time;
>             threshold = histogram[i]->count;
>   	}
>         if (!threshold)
>   	threshold = 1;
>         if (dump_file)
>   	{
>   	  gcov_type cumulated_time = 0, cumulated_size = 0;
> 
>             for (i = 0;
>   	       i < (int)histogram.length () && histogram[i]->count >= threshold;
>   	       i++)
>   	    {
> @@ -551,65 +823,101 @@ ipa_profile (void)
>   		   (int64_t)threshold,
>   		   cumulated_time * 100.0 / overall_time,
>   		   cumulated_size * 100.0 / overall_size);
>   	}
> 
>         if (in_lto_p)
>   	{
>   	  if (dump_file)
>   	    fprintf (dump_file, "Setting hotness threshold in LTO mode.\n");
>             set_hot_bb_threshold (threshold);
>   	}
>       }
>     histogram.release ();
>     histogram_pool.release ();
> 
> -  /* Produce speculative calls: we saved common target from porfiling into
> -     e->common_target_id.  Now, at link time, we can look up corresponding
> +  /* Produce speculative calls: we saved common target from profiling into
> +     e->target_id.  Now, at link time, we can look up corresponding
>        function node and produce speculative call.  */
> 
> +  gcc_checking_assert (call_sums);
> +
> +  if (dump_file)
> +    {
> +      if (!node_map_initialized)
> +	init_node_map (false);
> +      node_map_initialized = true;
> +
> +      ipa_profile_dump_all_summaries (dump_file);
> +    }
> +
>     FOR_EACH_DEFINED_FUNCTION (n)
>       {
>         bool update = false;
> 
>         if (!opt_for_fn (n->decl, flag_ipa_profile))
>   	continue;
> 
>         for (e = n->indirect_calls; e; e = e->next_callee)
>   	{
>   	  if (n->count.initialized_p ())
>   	    nindirect++;
> -	  if (e->indirect_info->common_target_id)
> +
> +	  speculative_call_summary *csum = call_sums->get_create (e);
> +	  if (!csum->is_empty ())
>   	    {
>   	      if (!node_map_initialized)
> -	        init_node_map (false);
> +		init_node_map (false);
>   	      node_map_initialized = true;
>   	      ncommon++;
> -	      n2 = find_func_by_profile_id (e->indirect_info->common_target_id);
> +
> +	      if (in_lto_p)
> +		{
> +		  if (dump_file)
> +		    {
> +		      fprintf (dump_file,
> +			       "Updating hotness threshold in LTO mode.\n");
> +		      fprintf (dump_file, "Updated min count: %" PRId64 "\n",
> +			       (int64_t) threshold
> +				 / csum->speculative_call_targets->length ());
> +		    }
> +		  set_hot_bb_threshold (threshold
> +		    / csum->speculative_call_targets->length ());
> +		}
> +
> +	      unsigned speculative_id = 0;
> +	      speculative_call_target *item;
> +	      /* The code below is not formatted yet for review convenience.
> +		 Move to a seprate small function is not easy as too many local
> +		 variables used in it.  Need format and remove this comments
> +		 once got approved.  */
> +	      FOR_EACH_VEC_SAFE_ELT (csum->speculative_call_targets, i, item)
> +	       {
> +	      bool speculative_found = false;
> +	      n2 = find_func_by_profile_id (item->target_id);
>   	      if (n2)
>   		{
>   		  if (dump_file)
>   		    {
>   		      fprintf (dump_file, "Indirect call -> direct call from"
>   			       " other module %s => %s, prob %3.2f\n",
>   			       n->dump_name (),
>   			       n2->dump_name (),
> -			       e->indirect_info->common_target_probability
> -			       / (float)REG_BR_PROB_BASE);
> +			       item->target_probability
> +				 / (float) REG_BR_PROB_BASE);
>   		    }
> -		  if (e->indirect_info->common_target_probability
> -		      < REG_BR_PROB_BASE / 2)
> +		  if (item->target_probability < REG_BR_PROB_BASE / 2)
>   		    {
>   		      nuseless++;
>   		      if (dump_file)
>   			fprintf (dump_file,
>   				 "Not speculating: probability is too low.\n");
>   		    }
>   		  else if (!e->maybe_hot_p ())
>   		    {
>   		      nuseless++;
>   		      if (dump_file)
>   			fprintf (dump_file,
>   				 "Not speculating: call is cold.\n");
>   		    }
>   		  else if (n2->get_availability () <= AVAIL_INTERPOSABLE
>   			   && n2->can_be_discarded_p ())
> @@ -641,44 +949,58 @@ ipa_profile (void)
>   		    }
>   		  else
>   		    {
>   		      /* Target may be overwritable, but profile says that
>   			 control flow goes to this particular implementation
>   			 of N2.  Speculate on the local alias to allow inlining.
>   		       */
>   		      if (!n2->can_be_discarded_p ())
>   			{
>   			  cgraph_node *alias;
>   			  alias = dyn_cast<cgraph_node *> (n2->noninterposable_alias ());
>   			  if (alias)
>   			    n2 = alias;
>   			}
>   		      nconverted++;
> -		      e->make_speculative
> -			(n2,
> -			 e->count.apply_probability
> -				     (e->indirect_info->common_target_probability));
> +		      e->make_speculative (n2,
> +					   e->count.apply_probability (
> +					     item->target_probability),
> +					   speculative_id);
>   		      update = true;
> +		      speculative_id++;
> +		      speculative_found = true;
>   		    }
>   		}
>   	      else
>   		{
>   		  if (dump_file)
>   		    fprintf (dump_file, "Function with profile-id %i not found.\n",
> -			     e->indirect_info->common_target_id);
> +			     item->target_id);
>   		  nunknown++;
>   		}
> +	      if (!speculative_found && !csum->is_empty ())
> +		{
> +		  /* Remove item from speculative_call_targets if no
> +		     speculative edge generated, rollback the iteration.  */
> +		  csum->speculative_call_targets->ordered_remove (i);
> +		  if (i)
> +		    i--;
> +		}
> +	       }
> +	      if (!csum->is_empty ())
> +		e->indirect_info->num_speculative_call_targets
> +		  = csum->speculative_call_targets->length ();
>   	    }
>   	 }
>          if (update)
>   	 ipa_update_overall_fn_summary (n);
>        }
>     if (node_map_initialized)
>       del_node_map ();
>     if (dump_file && nindirect)
>       fprintf (dump_file,
>   	     "%i indirect calls trained.\n"
>   	     "%i (%3.2f%%) have common target.\n"
>   	     "%i (%3.2f%%) targets was not found.\n"
>   	     "%i (%3.2f%%) targets had parameter count mismatch.\n"
>   	     "%i (%3.2f%%) targets was not in polymorphic call target list.\n"
>   	     "%i (%3.2f%%) speculations seems useless.\n"
> @@ -764,15 +1086,17 @@ public:
>     {}
> 
>     /* opt_pass methods: */
>     virtual bool gate (function *) { return flag_ipa_profile || in_lto_p; }
>     virtual unsigned int execute (function *) { return ipa_profile (); }
> 
>   }; // class pass_ipa_profile
> 
>   } // anon namespace
> 
>   ipa_opt_pass_d *
>   make_pass_ipa_profile (gcc::context *ctxt)
>   {
>     return new pass_ipa_profile (ctxt);
>   }
> +/* Tell the garbage collector about GTY markers in this source file.  */
> +#include "gt-ipa-profile.h"
> diff --git a/gcc/ipa-ref.h b/gcc/ipa-ref.h
> index 00af24c77db..b68661d45c8 100644
> --- a/gcc/ipa-ref.h
> +++ b/gcc/ipa-ref.h
> @@ -47,30 +47,31 @@ public:
>     bool cannot_lead_to_return ();
> 
>     /* Return true if reference may be used in address compare.  */
>     bool address_matters_p ();
> 
>     /* Return reference list this reference is in.  */
>     struct ipa_ref_list * referring_ref_list (void);
> 
>     /* Return reference list this reference is in.  */
>     struct ipa_ref_list * referred_ref_list (void);
> 
>     symtab_node *referring;
>     symtab_node *referred;
>     gimple *stmt;
>     unsigned int lto_stmt_uid;
> +  unsigned int speculative_id;
>     unsigned int referred_index;
>     ENUM_BITFIELD (ipa_ref_use) use:3;
>     unsigned int speculative:1;
>   };
> 
>   typedef struct ipa_ref ipa_ref_t;
>   typedef struct ipa_ref *ipa_ref_ptr;
> 
> 
>   /* List of references.  This is stored in both callgraph and varpool nodes.  */
>   struct GTY(()) ipa_ref_list
>   {
>   public:
>     /* Return first reference in list or NULL if empty.  */
>     struct ipa_ref *first_reference (void)
> diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
> index b5221cd41f9..00a79d35a51 100644
> --- a/gcc/lto-cgraph.c
> +++ b/gcc/lto-cgraph.c
> @@ -250,62 +250,58 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
>     if (!edge->indirect_unknown_callee)
>       {
>         ref = lto_symtab_encoder_lookup (encoder, edge->callee);
>         gcc_assert (ref != LCC_NOT_FOUND);
>         streamer_write_hwi_stream (ob->main_stream, ref);
>       }
> 
>     edge->count.stream_out (ob->main_stream);
> 
>     bp = bitpack_create (ob->main_stream);
>     uid = (!gimple_has_body_p (edge->caller->decl) || edge->caller->thunk.thunk_p
>   	 ? edge->lto_stmt_uid : gimple_uid (edge->call_stmt) + 1);
>     bp_pack_enum (&bp, cgraph_inline_failed_t,
>   	        CIF_N_REASONS, edge->inline_failed);
>     bp_pack_var_len_unsigned (&bp, uid);
> +  bp_pack_var_len_unsigned (&bp, edge->speculative_id);
>     bp_pack_value (&bp, edge->indirect_inlining_edge, 1);
>     bp_pack_value (&bp, edge->speculative, 1);
>     bp_pack_value (&bp, edge->call_stmt_cannot_inline_p, 1);
>     gcc_assert (!edge->call_stmt_cannot_inline_p
>   	      || edge->inline_failed != CIF_BODY_NOT_AVAILABLE);
>     bp_pack_value (&bp, edge->can_throw_external, 1);
>     bp_pack_value (&bp, edge->in_polymorphic_cdtor, 1);
>     if (edge->indirect_unknown_callee)
>       {
>         int flags = edge->indirect_info->ecf_flags;
>         bp_pack_value (&bp, (flags & ECF_CONST) != 0, 1);
>         bp_pack_value (&bp, (flags & ECF_PURE) != 0, 1);
>         bp_pack_value (&bp, (flags & ECF_NORETURN) != 0, 1);
>         bp_pack_value (&bp, (flags & ECF_MALLOC) != 0, 1);
>         bp_pack_value (&bp, (flags & ECF_NOTHROW) != 0, 1);
>         bp_pack_value (&bp, (flags & ECF_RETURNS_TWICE) != 0, 1);
>         /* Flags that should not appear on indirect calls.  */
>         gcc_assert (!(flags & (ECF_LOOPING_CONST_OR_PURE
>   			     | ECF_MAY_BE_ALLOCA
>   			     | ECF_SIBCALL
>   			     | ECF_LEAF
>   			     | ECF_NOVOPS)));
> +
> +      bp_pack_value (&bp, edge->indirect_info->num_speculative_call_targets,
> +		     16);
>       }
>     streamer_write_bitpack (&bp);
> -  if (edge->indirect_unknown_callee)
> -    {
> -      streamer_write_hwi_stream (ob->main_stream,
> -			         edge->indirect_info->common_target_id);
> -      if (edge->indirect_info->common_target_id)
> -	streamer_write_hwi_stream
> -	   (ob->main_stream, edge->indirect_info->common_target_probability);
> -    }
>   }
> 
>   /* Return if NODE contain references from other partitions.  */
> 
>   bool
>   referenced_from_other_partition_p (symtab_node *node, lto_symtab_encoder_t encoder)
>   {
>     int i;
>     struct ipa_ref *ref = NULL;
> 
>     for (i = 0; node->iterate_referring (i, ref); i++)
>       {
>         /* Ignore references from non-offloadable nodes while streaming NODE into
>   	 offload LTO section.  */
>         if (!ref->referring->need_lto_streaming)
> @@ -678,30 +674,31 @@ lto_output_ref (struct lto_simple_output_block *ob, struct ipa_ref *ref,
> 
>     bp = bitpack_create (ob->main_stream);
>     bp_pack_value (&bp, ref->use, 3);
>     bp_pack_value (&bp, ref->speculative, 1);
>     streamer_write_bitpack (&bp);
>     nref = lto_symtab_encoder_lookup (encoder, ref->referred);
>     gcc_assert (nref != LCC_NOT_FOUND);
>     streamer_write_hwi_stream (ob->main_stream, nref);
>     
>     node = dyn_cast <cgraph_node *> (ref->referring);
>     if (node)
>       {
>         if (ref->stmt)
>   	uid = gimple_uid (ref->stmt) + 1;
>         streamer_write_hwi_stream (ob->main_stream, uid);
> +      streamer_write_hwi_stream (ob->main_stream, ref->speculative_id);
>       }
>   }
> 
>   /* Stream out profile_summary to OB.  */
> 
>   static void
>   output_profile_summary (struct lto_simple_output_block *ob)
>   {
>     if (profile_info)
>       {
>         /* We do not output num and run_max, they are not used by
>            GCC profile feedback and they are difficult to merge from multiple
>            units.  */
>         unsigned runs = (profile_info->runs);
>         streamer_write_uhwi_stream (ob->main_stream, runs);
> @@ -1416,99 +1413,104 @@ input_ref (class lto_input_block *ib,
>   	   vec<symtab_node *> nodes)
>   {
>     symtab_node *node = NULL;
>     struct bitpack_d bp;
>     enum ipa_ref_use use;
>     bool speculative;
>     struct ipa_ref *ref;
> 
>     bp = streamer_read_bitpack (ib);
>     use = (enum ipa_ref_use) bp_unpack_value (&bp, 3);
>     speculative = (enum ipa_ref_use) bp_unpack_value (&bp, 1);
>     node = nodes[streamer_read_hwi (ib)];
>     ref = referring_node->create_reference (node, use);
>     ref->speculative = speculative;
>     if (is_a <cgraph_node *> (referring_node))
> -    ref->lto_stmt_uid = streamer_read_hwi (ib);
> +    {
> +      ref->lto_stmt_uid = streamer_read_hwi (ib);
> +      ref->speculative_id = streamer_read_hwi (ib);
> +    }
>   }
> 
>   /* Read an edge from IB.  NODES points to a vector of previously read nodes for
>      decoding caller and callee of the edge to be read.  If INDIRECT is true, the
>      edge being read is indirect (in the sense that it has
>      indirect_unknown_callee set).  */
> 
>   static void
>   input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
>   	    bool indirect)
>   {
>     struct cgraph_node *caller, *callee;
>     struct cgraph_edge *edge;
> -  unsigned int stmt_id;
> +  unsigned int stmt_id, speculative_id;
>     profile_count count;
>     cgraph_inline_failed_t inline_failed;
>     struct bitpack_d bp;
>     int ecf_flags = 0;
> 
>     caller = dyn_cast<cgraph_node *> (nodes[streamer_read_hwi (ib)]);
>     if (caller == NULL || caller->decl == NULL_TREE)
>       internal_error ("bytecode stream: no caller found while reading edge");
> 
>     if (!indirect)
>       {
>         callee = dyn_cast<cgraph_node *> (nodes[streamer_read_hwi (ib)]);
>         if (callee == NULL || callee->decl == NULL_TREE)
>   	internal_error ("bytecode stream: no callee found while reading edge");
>       }
>     else
>       callee = NULL;
> 
>     count = profile_count::stream_in (ib);
> 
>     bp = streamer_read_bitpack (ib);
>     inline_failed = bp_unpack_enum (&bp, cgraph_inline_failed_t, CIF_N_REASONS);
>     stmt_id = bp_unpack_var_len_unsigned (&bp);
> +  speculative_id = bp_unpack_var_len_unsigned (&bp);
> 
>     if (indirect)
>       edge = caller->create_indirect_edge (NULL, 0, count);
>     else
>       edge = caller->create_edge (callee, NULL, count);
> 
>     edge->indirect_inlining_edge = bp_unpack_value (&bp, 1);
>     edge->speculative = bp_unpack_value (&bp, 1);
>     edge->lto_stmt_uid = stmt_id;
> +  edge->speculative_id = speculative_id;
>     edge->inline_failed = inline_failed;
>     edge->call_stmt_cannot_inline_p = bp_unpack_value (&bp, 1);
>     edge->can_throw_external = bp_unpack_value (&bp, 1);
>     edge->in_polymorphic_cdtor = bp_unpack_value (&bp, 1);
>     if (indirect)
>       {
>         if (bp_unpack_value (&bp, 1))
>   	ecf_flags |= ECF_CONST;
>         if (bp_unpack_value (&bp, 1))
>   	ecf_flags |= ECF_PURE;
>         if (bp_unpack_value (&bp, 1))
>   	ecf_flags |= ECF_NORETURN;
>         if (bp_unpack_value (&bp, 1))
>   	ecf_flags |= ECF_MALLOC;
>         if (bp_unpack_value (&bp, 1))
>   	ecf_flags |= ECF_NOTHROW;
>         if (bp_unpack_value (&bp, 1))
>   	ecf_flags |= ECF_RETURNS_TWICE;
>         edge->indirect_info->ecf_flags = ecf_flags;
> -      edge->indirect_info->common_target_id = streamer_read_hwi (ib);
> -      if (edge->indirect_info->common_target_id)
> -        edge->indirect_info->common_target_probability = streamer_read_hwi (ib);
> +
> +      edge->indirect_info->num_speculative_call_targets
> +	= bp_unpack_value (&bp, 16);
>       }
>   }
> 
> 
>   /* Read a cgraph from IB using the info in FILE_DATA.  */
> 
>   static vec<symtab_node *>
>   input_cgraph_1 (struct lto_file_decl_data *file_data,
>   		class lto_input_block *ib)
>   {
>     enum LTO_symtab_tags tag;
>     vec<symtab_node *> nodes = vNULL;
>     symtab_node *node;
>     unsigned i;
> 
> diff --git a/gcc/predict.c b/gcc/predict.c
> index 67f850de17a..592db3421f3 100644
> --- a/gcc/predict.c
> +++ b/gcc/predict.c
> @@ -749,31 +749,30 @@ dump_prediction (FILE *file, enum br_predictor predictor, int probability,
>   	  fprintf (file, " hit ");
>   	  e->count ().dump (file);
>   	  fprintf (file, " (%.1f%%)", e->count ().to_gcov_type() * 100.0
>   		   / bb->count.to_gcov_type ());
>   	}
>       }
> 
>     fprintf (file, "\n");
> 
>     /* Print output that be easily read by analyze_brprob.py script. We are
>        interested only in counts that are read from GCDA files.  */
>     if (dump_file && (dump_flags & TDF_DETAILS)
>         && bb->count.precise_p ()
>         && reason == REASON_NONE)
>       {
> -      gcc_assert (e->count ().precise_p ());
>         fprintf (file, ";;heuristics;%s;%" PRId64 ";%" PRId64 ";%.1f;\n",
>   	       predictor_info[predictor].name,
>   	       bb->count.to_gcov_type (), e->count ().to_gcov_type (),
>   	       probability * 100.0 / REG_BR_PROB_BASE);
>       }
>   }
> 
>   /* Return true if STMT is known to be unlikely executed.  */
> 
>   static bool
>   unlikely_executed_stmt_p (gimple *stmt)
>   {
>     if (!is_gimple_call (stmt))
>       return false;
>     /* NORETURN attribute alone is not strong enough: exit() may be quite
> diff --git a/gcc/symtab.c b/gcc/symtab.c
> index f4317d02b71..c43bf613f08 100644
> --- a/gcc/symtab.c
> +++ b/gcc/symtab.c
> @@ -591,30 +591,31 @@ symtab_node::create_reference (symtab_node *referred_node,
>         ref->referred_index = 0;
> 
>         for (unsigned int i = 1; i < list2->referring.length (); i++)
>   	list2->referring[i]->referred_index = i;
>       }
>     else
>       {
>         list2->referring.safe_push (ref);
>         ref->referred_index = list2->referring.length () - 1;
>       }
> 
>     ref->referring = this;
>     ref->referred = referred_node;
>     ref->stmt = stmt;
>     ref->lto_stmt_uid = 0;
> +  ref->speculative_id = 0;
>     ref->use = use_type;
>     ref->speculative = 0;
> 
>     /* If vector was moved in memory, update pointers.  */
>     if (old_references != list->references->address ())
>       {
>         int i;
>         for (i = 0; iterate_reference(i, ref2); i++)
>   	ref2->referred_ref_list ()->referring[ref2->referred_index] = ref2;
>       }
>     return ref;
>   }
> 
>   ipa_ref *
>   symtab_node::maybe_create_reference (tree val, gimple *stmt)
> @@ -648,63 +649,66 @@ symtab_node::maybe_create_reference (tree val, gimple *stmt)
>   /* Clone all references from symtab NODE to this symtab_node.  */
> 
>   void
>   symtab_node::clone_references (symtab_node *node)
>   {
>     ipa_ref *ref = NULL, *ref2 = NULL;
>     int i;
>     for (i = 0; node->iterate_reference (i, ref); i++)
>       {
>         bool speculative = ref->speculative;
>         unsigned int stmt_uid = ref->lto_stmt_uid;
> 
>         ref2 = create_reference (ref->referred, ref->use, ref->stmt);
>         ref2->speculative = speculative;
>         ref2->lto_stmt_uid = stmt_uid;
> +      ref2->speculative_id = ref->speculative_id;
>       }
>   }
> 
>   /* Clone all referring from symtab NODE to this symtab_node.  */
> 
>   void
>   symtab_node::clone_referring (symtab_node *node)
>   {
>     ipa_ref *ref = NULL, *ref2 = NULL;
>     int i;
>     for (i = 0; node->iterate_referring(i, ref); i++)
>       {
>         bool speculative = ref->speculative;
>         unsigned int stmt_uid = ref->lto_stmt_uid;
> 
>         ref2 = ref->referring->create_reference (this, ref->use, ref->stmt);
>         ref2->speculative = speculative;
>         ref2->lto_stmt_uid = stmt_uid;
> +      ref2->speculative_id = ref->speculative_id;
>       }
>   }
> 
>   /* Clone reference REF to this symtab_node and set its stmt to STMT.  */
> 
>   ipa_ref *
>   symtab_node::clone_reference (ipa_ref *ref, gimple *stmt)
>   {
>     bool speculative = ref->speculative;
>     unsigned int stmt_uid = ref->lto_stmt_uid;
>     ipa_ref *ref2;
> 
>     ref2 = create_reference (ref->referred, ref->use, stmt);
>     ref2->speculative = speculative;
>     ref2->lto_stmt_uid = stmt_uid;
> +  ref2->speculative_id = ref->speculative_id;
>     return ref2;
>   }
> 
>   /* Find the structure describing a reference to REFERRED_NODE
>      and associated with statement STMT.  */
> 
>   ipa_ref *
>   symtab_node::find_reference (symtab_node *referred_node,
>   			     gimple *stmt, unsigned int lto_stmt_uid)
>   {
>     ipa_ref *r = NULL;
>     int i;
> 
>     for (i = 0; iterate_reference (i, r); i++)
>       if (r->referred == referred_node
> @@ -735,30 +739,31 @@ symtab_node::remove_stmt_references (gimple *stmt)
>      Those are not maintained during inlining & cloning.
>      The exception are speculative references that are updated along
>      with callgraph edges associated with them.  */
> 
>   void
>   symtab_node::clear_stmts_in_references (void)
>   {
>     ipa_ref *r = NULL;
>     int i;
> 
>     for (i = 0; iterate_reference (i, r); i++)
>       if (!r->speculative)
>         {
>   	r->stmt = NULL;
>   	r->lto_stmt_uid = 0;
> +	r->speculative_id = 0;
>         }
>   }
> 
>   /* Remove all references in ref list.  */
> 
>   void
>   symtab_node::remove_all_references (void)
>   {
>     while (vec_safe_length (ref_list.references))
>       ref_list.references->last ().remove_reference ();
>     vec_free (ref_list.references);
>   }
> 
>   /* Remove all referring items in ref list.  */
> 
> diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
> new file mode 100644
> index 00000000000..a13b08cd60e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
> @@ -0,0 +1,33 @@
> +/* { dg-require-effective-target lto } */
> +/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
> +/* { dg-require-profiling "-fprofile-generate" } */
> +/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate" } */
> +
> +#include <stdio.h>
> +
> +typedef int (*fptr) (int);
> +int
> +one (int a);
> +
> +int
> +two (int a);
> +
> +fptr table[] = {&one, &two};
> +
> +int
> +main()
> +{
> +  int i, x;
> +  fptr p = &one;
> +
> +  x = one (3);
> +
> +  for (i = 0; i < 350000000; i++)
> +    {
> +      x = (*p) (3);
> +      p = table[x];
> +    }
> +  printf ("done:%d\n", x);
> +}
> +
> +/* { dg-final-use-not-autofdo { scan-pgo-wpa-ipa-dump "2 \\(200.00%\\) speculations produced." "profile_estimate" } } */
> diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
> new file mode 100644
> index 00000000000..a8c6e365fb9
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
> @@ -0,0 +1,22 @@
> +/* It seems there is no way to avoid the other source of mulitple
> +   source testcase from being compiled independently.  Just avoid
> +   error.  */
> +#ifdef DOJOB
> +int
> +one (int a)
> +{
> +  return 1;
> +}
> +
> +int
> +two (int a)
> +{
> +  return 0;
> +}
> +#else
> +int
> +main()
> +{
> +  return 0;
> +}
> +#endif
> diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
> new file mode 100644
> index 00000000000..9b996fcf0ed
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
> @@ -0,0 +1,40 @@
> +/* { dg-require-effective-target lto } */
> +/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
> +/* { dg-require-profiling "-fprofile-generate" } */
> +/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate" } */
> +
> +#include <stdio.h>
> +
> +typedef int (*fptr) (int);
> +int
> +one (int a);
> +
> +int
> +two (int a);
> +
> +fptr table[] = {&one, &two};
> +
> +int foo ()
> +{
> +  int i, x;
> +  fptr p = &one;
> +
> +  x = one (3);
> +
> +  for (i = 0; i < 350000000; i++)
> +    {
> +      x = (*p) (3);
> +      p = table[x];
> +    }
> +  return x;
> +}
> +
> +int
> +main()
> +{
> +  int x = foo ();
> +  printf ("done:%d\n", x);
> +}
> +
> +/* { dg-final-use-not-autofdo { scan-pgo-wpa-ipa-dump "2 \\(200.00%\\) speculations produced." "profile_estimate" } } */
> +
> diff --git a/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
> new file mode 100644
> index 00000000000..063996c71df
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
> @@ -0,0 +1,37 @@
> +/* { dg-require-profiling "-fprofile-generate" } */
> +/* { dg-options "-O2 -fdump-ipa-profile_estimate" } */
> +
> +#include <stdio.h>
> +
> +typedef int (*fptr) (int);
> +int
> +one (int a)
> +{
> +  return 1;
> +}
> +
> +int
> +two (int a)
> +{
> +  return 0;
> +}
> +
> +fptr table[] = {&one, &two};
> +
> +int
> +main()
> +{
> +  int i, x;
> +  fptr p = &one;
> +
> +  one (3);
> +
> +  for (i = 0; i < 350000000; i++)
> +    {
> +      x = (*p) (3);
> +      p = table[x];
> +    }
> +  printf ("done:%d\n", x);
> +}
> +
> +/* { dg-final-use-not-autofdo { scan-ipa-dump "2 \\(200.00%\\) speculations produced." "profile_estimate" } } */
> diff --git a/gcc/testsuite/lib/scandump.exp b/gcc/testsuite/lib/scandump.exp
> index 42f5c01aa60..8b7cd7cfc16 100644
> --- a/gcc/testsuite/lib/scandump.exp
> +++ b/gcc/testsuite/lib/scandump.exp
> @@ -58,30 +58,31 @@ proc scan-dump { args } {
>           }
>       }
> 
>       set testcase [testname-for-summary]
>       # The name might include a list of options; extract the file name.
>       set filename [lindex $testcase 0]
> 
>       set printable_pattern [make_pattern_printable [lindex $args 1]]
>       set suf [dump-suffix [lindex $args 2]]
>       set testname "$testcase scan-[lindex $args 0]-dump $suf \"$printable_pattern\""
>       set src [file tail $filename]
>       set dumpbase [dump-base $src [lindex $args 3]]
>       set output_file "[glob -nocomplain $dumpbase.[lindex $args 2]]"
>       if { $output_file == "" } {
>   	verbose -log "$testcase: dump file does not exist"
> +	verbose -log "dump file: $dumpbase.$suf"
>   	unresolved "$testname"
>   	return
>       }
> 
>       set fd [open $output_file r]
>       set text [read $fd]
>       close $fd
> 
>       if [regexp -- [lindex $args 1] $text] {
>   	pass "$testname"
>       } else {
>   	fail "$testname"
>       }
>   }
> 
> diff --git a/gcc/testsuite/lib/scanwpaipa.exp b/gcc/testsuite/lib/scanwpaipa.exp
> index b5549fd688e..8aafd6c82e8 100644
> --- a/gcc/testsuite/lib/scanwpaipa.exp
> +++ b/gcc/testsuite/lib/scanwpaipa.exp
> @@ -33,30 +33,53 @@ proc scan-wpa-ipa-dump { args } {
>       }
>       if { [llength $args] > 3 } {
>   	error "scan-wpa-ipa-dump: too many arguments"
>   	return
>       }
>       if { [llength $args] >= 3 } {
>   	scan-dump "wpa-ipa" [lindex $args 0] \
>   		  "\[0-9\]\[0-9\]\[0-9\]i.[lindex $args 1]" ".exe.wpa" \
>   		  [lindex $args 2]
>       } else {
>   	scan-dump "wpa-ipa" [lindex $args 0] \
>   		  "\[0-9\]\[0-9\]\[0-9\]i.[lindex $args 1]" ".exe.wpa"
>       }
>   }
> 
> +# Argument 0 is the regexp to match
> +# Argument 1 is the name of the dumped ipa pass
> +# Argument 2 handles expected failures and the like
> +proc scan-pgo-wpa-ipa-dump { args } {
> +
> +    if { [llength $args] < 2 } {
> +	error "scan-pgo-wpa-ipa-dump: too few arguments"
> +	return
> +    }
> +    if { [llength $args] > 3 } {
> +	error "scan-pgo-wpa-ipa-dump: too many arguments"
> +	return
> +    }
> +    if { [llength $args] >= 3 } {
> +	scan-dump "pgo-wpa-ipa" [lindex $args 0] \
> +		  "\[0-9\]\[0-9\]\[0-9\]i.[lindex $args 1]" ".x02.wpa" \
> +		  [lindex $args 2]
> +    } else {
> +	scan-dump "pgo-wpa-ipa" [lindex $args 0] \
> +		  "\[0-9\]\[0-9\]\[0-9\]i.[lindex $args 1]" ".x02.wpa"
> +    }
> +}
> +
>   # Call pass if pattern is present given number of times, otherwise fail.
>   # Argument 0 is the regexp to match
>   # Argument 1 is number of times the regexp must be found
>   # Argument 2 is the name of the dumped ipa pass
>   # Argument 3 handles expected failures and the like
>   proc scan-wpa-ipa-dump-times { args } {
> 
>       if { [llength $args] < 3 } {
>   	error "scan-wpa-ipa-dump-times: too few arguments"
>   	return
>       }
>       if { [llength $args] > 4 } {
>   	error "scan-wpa-ipa-dump-times: too many arguments"
>   	return
>       }
> diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
> index 720f50eefec..ee024388cf7 100644
> --- a/gcc/tree-inline.c
> +++ b/gcc/tree-inline.c
> @@ -2185,30 +2185,52 @@ copy_bb (copy_body_data *id, basic_block bb,
>   		      edge = edge->clone (id->dst_node, call_stmt,
>   					  gimple_uid (stmt),
>   					  num, den,
>   					  true);
> 
>   		      /* Speculative calls consist of two edges - direct and
>   			 indirect.  Duplicate the whole thing and distribute
>   			 frequencies accordingly.  */
>   		      if (edge->speculative)
>   			{
>   			  struct cgraph_edge *direct, *indirect;
>   			  struct ipa_ref *ref;
> 
>   			  gcc_assert (!edge->indirect_unknown_callee);
>   			  old_edge->speculative_call_info (direct, indirect, ref);
> +			  while (old_edge->next_callee
> +				 && old_edge->next_callee->speculative
> +				 && indirect->num_speculative_call_targets_p ()
> +				      > 1)
> +			    {
> +			      id->dst_node->clone_reference (ref, stmt);
> +
> +			      edge = old_edge->next_callee;
> +			      edge = edge->clone (id->dst_node, call_stmt,
> +						  gimple_uid (stmt), num, den,
> +						  true);
> +			      old_edge = old_edge->next_callee;
> +			      gcc_assert (!edge->indirect_unknown_callee);
> +
> +			      /* If the indirect edge has multiple speculative
> +				 calls, iterate through all direct calls
> +				 associated to the speculative call and clone
> +				 all related direct edges before cloning the
> +				 related indirect edge.  */
> +			      old_edge->speculative_call_info (direct, indirect,
> +							       ref);
> +			    }
> 
>   			  profile_count indir_cnt = indirect->count;
>   			  indirect = indirect->clone (id->dst_node, call_stmt,
>   						      gimple_uid (stmt),
>   						      num, den,
>   						      true);
> 
>   			  profile_probability prob
>   			     = indir_cnt.probability_in (old_cnt + indir_cnt);
>   			  indirect->count
>   			     = copy_basic_block->count.apply_probability (prob);
>   			  edge->count = copy_basic_block->count - indirect->count;
>   			  id->dst_node->clone_reference (ref, stmt);
>   			}
>   		      else
> diff --git a/gcc/value-prof.c b/gcc/value-prof.c
> index cc3542f0295..f64f515c1ee 100644
> --- a/gcc/value-prof.c
> +++ b/gcc/value-prof.c
> @@ -94,31 +94,31 @@ along with GCC; see the file COPYING3.  If not see
>      Limitations / FIXME / TODO:
>      * Only one histogram of each type can be associated with a statement.
>      * Some value profile transformations are done in builtins.c (?!)
>      * Updating of histograms needs some TLC.
>      * The value profiling code could be used to record analysis results
>        from non-profiling (e.g. VRP).
>      * Adding new profilers should be simplified, starting with a cleanup
>        of what-happens-where and with making gimple_find_values_to_profile
>        and gimple_value_profile_transformations table-driven, perhaps...
>   */
> 
>   static bool gimple_divmod_fixed_value_transform (gimple_stmt_iterator *);
>   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 gimple_ic_transform (gimple_stmt_iterator *);
> 
>   /* Allocate histogram value.  */
> 
>   histogram_value
>   gimple_alloc_histogram_value (struct function *fun ATTRIBUTE_UNUSED,
>   			      enum hist_type type, gimple *stmt, tree value)
>   {
>      histogram_value hist = (histogram_value) xcalloc (1, sizeof (*hist));
>      hist->hvalue.value = value;
>      hist->hvalue.stmt = stmt;
>      hist->type = type;
>      return hist;
>   }
> 
>   /* Hash value for histogram.  */
> @@ -604,42 +604,44 @@ gimple_value_profile_transformations (void)
>   	      fprintf (dump_file, "Trying transformations on stmt ");
>   	      print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
>   	      dump_histograms_for_stmt (cfun, dump_file, stmt);
>   	    }
> 
>   	  /* Transformations:  */
>   	  /* The order of things in this conditional controls which
>   	     transformation is used when more than one is applicable.  */
>   	  /* It is expected that any code added by the transformations
>   	     will be added before the current statement, and that the
>   	     current statement remain valid (although possibly
>   	     modified) upon return.  */
>   	  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;
>   	      /* Original statement may no longer be in the same block. */
>   	      if (bb != gimple_bb (stmt))
>   		{
>   	          bb = gimple_bb (stmt);
>   		  gsi = gsi_for_stmt (stmt);
>   		}
>   	    }
> +
> +	  /* The function never thansforms a GIMPLE statement.  */
> +	  gimple_ic_transform (&gsi);
>           }
>       }
> 
>     return changed;
>   }
> 
>   /* Generate code for transformation 1 (with parent gimple assignment
>      STMT and probability of taking the optimal path PROB, which is
>      equivalent to COUNT/ALL within roundoff error).  This generates the
>      result into a temp and returns the temp; it does not replace or
>      alter the original STMT.  */
> 
>   static tree
>   gimple_divmod_fixed_value (gassign *stmt, tree value, profile_probability prob,
>   			   gcov_type count, gcov_type all)
> @@ -1374,92 +1376,97 @@ gimple_ic (gcall *icall_stmt, struct cgraph_node *direct_call,
>   	e = make_edge (dcall_bb, e_eh->dest, e_eh->flags);
>   	e->probability = e_eh->probability;
>   	for (gphi_iterator psi = gsi_start_phis (e_eh->dest);
>   	     !gsi_end_p (psi); gsi_next (&psi))
>   	  {
>   	    gphi *phi = psi.phi ();
>   	    SET_USE (PHI_ARG_DEF_PTR_FROM_EDGE (phi, e),
>   		     PHI_ARG_DEF_FROM_EDGE (phi, e_eh));
>   	  }
>          }
>     if (!stmt_could_throw_p (cfun, dcall_stmt))
>       gimple_purge_dead_eh_edges (dcall_bb);
>     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:
> - */
> +/* There maybe multiple indirect targets in histogram.  Check every
> +   indirect/virtual call if callee function exists, if not exist, leave it to
> +   LTO stage for later process.  Modify code of this indirect call to an if-else
> +   structure in ipa-profile finally.  */
> 
> -static bool
> +static void
>   gimple_ic_transform (gimple_stmt_iterator *gsi)
>   {
>     gcall *stmt;
>     histogram_value histogram;
>     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;
> +    return;
> 
> -  if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
> -				  &count, &all))
> -    return false;
> +  count = 0;
> +  all = histogram->hvalue.counters[0];
> 
> -  if (4 * count <= 3 * all)
> -    return false;
> +  for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
> +    {
> +      if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
> +				      &count, &all, j))
> +	return;
> 
> -  direct_call = find_func_by_profile_id ((int)val);
> +      /* Minimum probability.  should be higher than 25%.  */
> +      if (4 * count <= all)
> +	return;
> 
> -  if (direct_call == NULL)
> -    {
> -      if (val)
> +      direct_call = find_func_by_profile_id ((int) val);
> +
> +      if (direct_call == NULL)
>   	{
> -	  if (dump_enabled_p ())
> -	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
> -			     "Indirect call -> direct call from other "
> -			     "module %T=> %i (will resolve only with LTO)\n",
> -			     gimple_call_fn (stmt), (int)val);
> +	  if (val)
> +	    {
> +	      if (dump_enabled_p ())
> +		dump_printf_loc (
> +		  MSG_MISSED_OPTIMIZATION, stmt,
> +		  "Indirect call -> direct call from other "
> +		  "module %T=> %i (will resolve only with LTO)\n",
> +		  gimple_call_fn (stmt), (int) val);
> +	    }
> +	  return;
>   	}
> -      return false;
> -    }
> 
> -  if (dump_enabled_p ())
> -    {
> -      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
> -		       "Indirect call -> direct call "
> -		       "%T => %T transformation on insn postponed\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_enabled_p ())
> +	{
> +	  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
> +			   "Indirect call -> direct call "
> +			   "%T => %T transformation on insn postponed\n",
> +			   gimple_call_fn (stmt), direct_call->decl);
> +	  dump_printf_loc (MSG_NOTE, stmt,
> +			   "hist->count %" PRId64 " hist->all %" PRId64 "\n",
> +			   count, all);
> +	}
>       }
> -
> -  return true;
>   }
> 
>   /* Return true if the stringop CALL shall be profiled.  SIZE_ARG be
>      set to the argument index for the size of the string operation.  */
> 
>   static bool
>   interesting_stringop_to_profile_p (gcall *call, int *size_arg)
>   {
>     enum built_in_function fcode;
> 
>     fcode = DECL_FUNCTION_CODE (gimple_call_fndecl (call));
>     switch (fcode)
>       {
>        case BUILT_IN_MEMCPY:
>        case BUILT_IN_MEMPCPY:
> diff --git a/gcc/value-prof.h b/gcc/value-prof.h
> index 77c06f60096..b3eeb57d37d 100644
> --- a/gcc/value-prof.h
> +++ b/gcc/value-prof.h
> @@ -77,31 +77,30 @@ histogram_value gimple_alloc_histogram_value (struct function *, enum hist_type,
>   					      tree value = NULL_TREE);
>   histogram_value gimple_histogram_value (struct function *, gimple *);
>   histogram_value gimple_histogram_value_of_type (struct function *, gimple *,
>   						enum hist_type);
>   void gimple_add_histogram_value (struct function *, gimple *, histogram_value);
>   void dump_histograms_for_stmt (struct function *, FILE *, gimple *);
>   void gimple_remove_histogram_value (struct function *, gimple *, histogram_value);
>   void gimple_remove_stmt_histograms (struct function *, gimple *);
>   void gimple_duplicate_stmt_histograms (struct function *, gimple *,
>   				       struct function *, gimple *);
>   void gimple_move_stmt_histograms (struct function *, gimple *, gimple *);
>   void verify_histograms (void);
>   void free_histograms (function *);
>   void stringop_block_profile (gimple *, unsigned int *, HOST_WIDE_INT *);
>   gcall *gimple_ic (gcall *, struct cgraph_node *, profile_probability);
> -bool check_ic_target (gcall *, struct cgraph_node *);
>   bool get_nth_most_common_value (gimple *stmt, const char *counter_type,
>   				histogram_value hist, gcov_type *value,
>   				gcov_type *count, gcov_type *all,
>   				unsigned n = 0);
> 
>   /* In tree-profile.c.  */
>   extern void gimple_init_gcov_profiler (void);
>   extern void gimple_gen_edge_profiler (int, edge);
>   extern void gimple_gen_interval_profiler (histogram_value, unsigned);
>   extern void gimple_gen_pow2_profiler (histogram_value, unsigned);
>   extern void gimple_gen_topn_values_profiler (histogram_value, unsigned);
>   extern void gimple_gen_ic_profiler (histogram_value, unsigned);
>   extern void gimple_gen_ic_func_profiler (void);
>   extern void gimple_gen_time_profiler (unsigned);
>   extern void gimple_gen_average_profiler (histogram_value, unsigned);
> 

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: *Ping* Re: [PATCH v6] Missed function specialization + partial devirtualization
  2019-12-18  2:33                               ` *Ping* " luoxhu
@ 2019-12-19 13:22                                 ` Jan Hubicka
  2019-12-26 13:42                                   ` [PATCH v7] " luoxhu
  0 siblings, 1 reply; 38+ messages in thread
From: Jan Hubicka @ 2019-12-19 13:22 UTC (permalink / raw)
  To: luoxhu; +Cc: Martin Liška, gcc-patches, segher, wschmidt

> > gcc/ChangeLog
> > 
> > 	2019-12-02  Xiong Hu Luo  <luoxhu@linux.ibm.com>
> > 
> > 	PR ipa/69678
> > 	* Makefile.in (GTFILES): Add ipa-profile.c.
> > 	* cgraph.c (symbol_table::create_edge): Init speculative_id.
> > 	(cgraph_edge::make_speculative): Add param for setting speculative_id.
> > 	(cgraph_edge::speculative_call_info): Update comments and find reference
> > 	by speculative_id for multiple indirect targets.
> > 	(cgraph_edge::resolve_speculation): Decrease the speculations
> > 	for indirect edge, drop it's speculative if not direct target
> > 	left. Update comments.
> > 	(cgraph_edge::redirect_call_stmt_to_callee): Likewise.
> > 	(cgraph_node::dump): Print num_speculative_call_targets.
> > 	(cgraph_node::verify_node): Don't report error if speculative
> > 	edge not include statement.
> > 	(cgraph_edge::num_speculative_call_targets_p): New function.
> > 	* cgraph.h (int common_target_id): Remove.
> > 	(int common_target_probability): Remove.
> > 	(num_speculative_call_targets): New variable.
> > 	(make_speculative): Add param for setting speculative_id.
> > 	(cgraph_edge::num_speculative_call_targets_p): New declare.
> > 	(speculative_id): New variable.
> > 	* cgraphclones.c (cgraph_node::create_clone): Clone speculative_id.
> > 	* ipa-profile.c (struct speculative_call_target): New struct.
> > 	(class speculative_call_summary): New class.
> > 	(class speculative_call_summaries): New class.
> > 	(call_sums): New variable.
> > 	(ipa_profile_generate_summary): Generate indirect multiple targets summaries.
> > 	(ipa_profile_write_edge_summary): New function.
> > 	(ipa_profile_write_summary): Stream out indirect multiple targets summaries.
> > 	(ipa_profile_dump_all_summaries): New function.
> > 	(ipa_profile_read_edge_summary): New function.
> > 	(ipa_profile_read_summary_section): New function.
> > 	(ipa_profile_read_summary): Stream in indirect multiple targets summaries.
> > 	(ipa_profile): Generate num_speculative_call_targets from
> > 	summaries.
> > 	* ipa-ref.h (speculative_id): New variable.
> > 	* lto-cgraph.c (lto_output_edge): Remove indirect common_target_id and
> > 	common_target_probability.   Stream out speculative_id and
> > 	num_speculative_call_targets.
> > 	(input_edge): Likewise.
> > 	* predict.c (dump_prediction): Remove edges count assert to be
> > 	precise.
> > 	* symtab.c (symtab_node::create_reference): Init speculative_id.
> > 	(symtab_node::clone_references): Clone speculative_id.
> > 	(symtab_node::clone_referring): Clone speculative_id.
> > 	(symtab_node::clone_reference): Clone speculative_id.
> > 	(symtab_node::clear_stmts_in_references): Clear speculative_id.
> > 	* tree-inline.c (copy_bb): Duplicate all the speculative edges
> > 	if indirect call contains multiple speculative targets.
> > 	* tree-profile.c (gimple_gen_ic_profiler): Use the new variable
> > 	__gcov_indirect_call.counters and __gcov_indirect_call.callee.
> > 	(gimple_gen_ic_func_profiler): Likewise.
> > 	(pass_ipa_tree_profile::gate): Fix comment typos.
> > 	* value-prof.h  (check_ic_target): Remove.
> > 	* value-prof.c  (gimple_value_profile_transformations):
> > 	Use void function gimple_ic_transform.
> > 	* value-prof.c  (gimple_ic_transform): Handle topn case.
> > 	Fix comment typos.  Change it to a void function.
> > 
> > gcc/testsuite/ChangeLog
> > 
> > 	2019-12-02  Xiong Hu Luo  <luoxhu@linux.ibm.com>
> > 
> > 	PR ipa/69678
> > 	* gcc.dg/tree-prof/indir-call-prof-topn.c: New testcase.
> > 	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c: New testcase.
> > 	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c: New testcase.
> > 	* gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c: New testcase.
> > 	* lib/scandump.exp: Dump executable file name.
> > 	* lib/scanwpaipa.exp: New scan-pgo-wap-ipa-dump.

> > -cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
> > +cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count,
> > +			       unsigned int speculative_id)

Please document speculative_id parameter.
> > +/* Speculative calls represent a transformation of indirect calls
> > +   which may be later inserted into gimple in the following form:
> > 
> > -   Given speculative call edge, return all three components.
> > +   if (call_dest == target1)
> > +   target1 ();
> > +   else if (call_dest == target2)
> > +   target2 ();
> > +   else
> > +   call_dest ();
> > +
> > +   This is a win in case when target1 and target2 are common values for

  ... in the case ...
I guess :)
> > -/* Collect histogram from CFG profiles.  */
> > +/* Structure containing speculative target information from profile.  */
> > +
> > +struct GTY (()) speculative_call_target
There is no need to place this in the garbage collector, so remove GTY.
> > +{
> > +  speculative_call_target (unsigned int id, int prob)
> > +    : target_id (id), target_probability (prob)
> > +  {
> > +  }
> > +
> > +  /* Profile_id of target obtained from profile.  */
> > +  unsigned int target_id;
> > +  /* Probability that call will land in function with target_id.  */
> > +  int target_probability;
> > +};
> > +
> > +class GTY ((for_user)) speculative_call_summary
Similarly here, drop GTY marker.
> > +{
> > +public:
> > +  speculative_call_summary () : speculative_call_targets ()
> > +  {}
> > +
> > +  vec<speculative_call_target, va_gc> *speculative_call_targets;
Making this auto_vec will save the need to deallocate explicitly.
> > +  ~speculative_call_summary ();
> > +
> > +  void dump (FILE *f);
> > +
> > +  /* Check whether this is a empty summary.  */
> > +  bool is_empty ();
> > +};
> > +
> > +  /* Class to manage call summaries.  */
> > +
> > +class GTY ((user)) ipa_profile_call_summaries
> > +  : public call_summary<speculative_call_summary *>
Remove GTY and make this non-ggc
> > +{
> > +public:
> > +  ipa_profile_call_summaries (symbol_table *table, bool ggc)
> > +    : call_summary<speculative_call_summary *> (table, ggc)
> > +  {}
Do we need the empty constructor at all?
> > +
> > +  /* Duplicate info when an edge is cloned.  */
> > +  virtual void duplicate (cgraph_edge *, cgraph_edge *,
> > +			  speculative_call_summary *old_sum,
> > +			  speculative_call_summary *new_sum);
> > +};
> > +
> > +static GTY (()) ipa_profile_call_summaries *call_sums = NULL;
> > +
> > +speculative_call_summary::~speculative_call_summary ()
> > +{
> > +  if (speculative_call_targets)
> > +    {
> > +      vec_free (speculative_call_targets);
> > +      speculative_call_targets = NULL;
> > +    }
> > +}

This desctuctor is unnecesary if you turn call tarets to auto_vec.
> > +
> > +/* Dump all information in speculative call summary to F.  */
> > +
> > +void
> > +speculative_call_summary::dump (FILE *f)
> > +{
> > +  speculative_call_target *item;
> > +  cgraph_node *n2;
> > +  unsigned int i;
> > +
> > +  FOR_EACH_VEC_SAFE_ELT (speculative_call_targets, i, item)
> > +    {
> > +      n2 = find_func_by_profile_id (item->target_id);
> > +      if (n2)
> > +	fprintf (f, "    The %i speculative target is %s with prob %3.2f\n", i,
> > +		 n2->dump_name (),
> > +		 item->target_probability / (float) REG_BR_PROB_BASE);
> > +      else
> > +	fprintf (f, "    The %i speculative target is %u with prob %3.2f\n", i,
> > +		 item->target_id,
> > +		 item->target_probability / (float) REG_BR_PROB_BASE);
> > +    }
> > +}
> > +
> > +/* Check whether this is a empty summary.  */
> > +bool
> > +speculative_call_summary::is_empty ()
> > +{
> > +  return speculative_call_targets == NULL
> > +	 || speculative_call_targets->is_empty ();
> > +}

We probably do not want to have empty summaries around, so the second
test chould be assert?
> > +
> > +/* Duplicate info when an edge is cloned.  */
> > +
> > +void
> > +ipa_profile_call_summaries::duplicate (cgraph_edge *, cgraph_edge *,
> > +				  speculative_call_summary *old_sum,
> > +				  speculative_call_summary *new_sum)
> > +{
> > +  if (!old_sum || !old_sum->speculative_call_targets)
> > +    return;
> > +
> > +  speculative_call_target *item;
> > +  unsigned int i;
> > +
> > +  FOR_EACH_VEC_SAFE_ELT (old_sum->speculative_call_targets, i, item)
> > +    {
> > +      vec_safe_push (new_sum->speculative_call_targets, *item);
> > +    }
vec_copy will do.
> > diff --git a/gcc/ipa-ref.h b/gcc/ipa-ref.h
> > index 00af24c77db..b68661d45c8 100644
> > --- a/gcc/ipa-ref.h
> > +++ b/gcc/ipa-ref.h
> > @@ -47,30 +47,31 @@ public:
> >     bool cannot_lead_to_return ();
> > 
> >     /* Return true if reference may be used in address compare.  */
> >     bool address_matters_p ();
> > 
> >     /* Return reference list this reference is in.  */
> >     struct ipa_ref_list * referring_ref_list (void);
> > 
> >     /* Return reference list this reference is in.  */
> >     struct ipa_ref_list * referred_ref_list (void);
> > 
> >     symtab_node *referring;
> >     symtab_node *referred;
> >     gimple *stmt;
> >     unsigned int lto_stmt_uid;
> > +  unsigned int speculative_id;
Please add documentation. 
I would also make them 16bit and packed with bitfeilds to be consistent
with cgraph edges.  
ipa_refs are quite critical for memory use of WPA stage. I see it will
not save memory now but it will help us to not forget about the fact
that they have limited value range in future.
> > 
> >   		      /* Speculative calls consist of two edges - direct and
> >   			 indirect.  Duplicate the whole thing and distribute
> >   			 frequencies accordingly.  */
This comment is out of date now.
> >   		      if (edge->speculative)
> >   			{
> >   			  struct cgraph_edge *direct, *indirect;
> >   			  struct ipa_ref *ref;
> > 
> >   			  gcc_assert (!edge->indirect_unknown_callee);
> >   			  old_edge->speculative_call_info (direct, indirect, ref);
> > +			  while (old_edge->next_callee
> > +				 && old_edge->next_callee->speculative
> > +				 && indirect->num_speculative_call_targets_p ()
> > +				      > 1)
> > +			    {
> > +			      id->dst_node->clone_reference (ref, stmt);
> > +
> > +			      edge = old_edge->next_callee;
> > +			      edge = edge->clone (id->dst_node, call_stmt,
> > +						  gimple_uid (stmt), num, den,
> > +						  true);
> > +			      old_edge = old_edge->next_callee;
> > +			      gcc_assert (!edge->indirect_unknown_callee);
> > +
> > +			      /* If the indirect edge has multiple speculative
> > +				 calls, iterate through all direct calls
> > +				 associated to the speculative call and clone
> > +				 all related direct edges before cloning the
> > +				 related indirect edge.  */
> > +			      old_edge->speculative_call_info (direct, indirect,
> > +							       ref);
> > +			    }
> > 
> >   			  profile_count indir_cnt = indirect->count;
> >   			  indirect = indirect->clone (id->dst_node, call_stmt,
> >   						      gimple_uid (stmt),
> >   						      num, den,
> >   						      true);
> > 
> >   			  profile_probability prob
> >   			     = indir_cnt.probability_in (old_cnt + indir_cnt);
> >   			  indirect->count
> >   			     = copy_basic_block->count.apply_probability (prob);

The udpating of indirect edges needs to be done in loop as well so we
update all of them.

I think you are also not removing the common_target and
common_target_probability from cgraph_edge.
There is now code in ipa-utils merging the histograms. You will need to
update that to your representation. It should not be hard - it either
copies all the values from target function or merges them with given
probability scales.

Honza

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH v7] Missed function specialization + partial devirtualization
  2019-12-19 13:22                                 ` Jan Hubicka
@ 2019-12-26 13:42                                   ` luoxhu
  2020-01-10 11:23                                     ` Jan Hubicka
  0 siblings, 1 reply; 38+ messages in thread
From: luoxhu @ 2019-12-26 13:42 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Martin Liška, gcc-patches, segher, wschmidt

>>>    			  profile_count indir_cnt = indirect->count;
>>>    			  indirect = indirect->clone (id->dst_node, call_stmt,
>>>    						      gimple_uid (stmt),
>>>    						      num, den,
>>>    						      true);
>>>
>>>    			  profile_probability prob
>>>    			     = indir_cnt.probability_in (old_cnt + indir_cnt);
>>>    			  indirect->count
>>>    			     = copy_basic_block->count.apply_probability (prob);
> 
> The udpating of indirect edges needs to be done in loop as well so we
> update all of them.

One indirect edge may have multiple speculative direct edge, the indirect edge must
be out of the loop after all direct edges are cloned.  Added comments in code.

> 
> I think you are also not removing the common_target and
> common_target_probability from cgraph_edge.
> There is now code in ipa-utils merging the histograms. You will need to
> update that to your representation. It should not be hard - it either
> copies all the values from target function or merges them with given
> probability scales.

This seems a bit tricky for me.  ipa-icf runs after ipa-profile, the reason why 
remove <common_target_id, common_target_probability> is they are never used in
later passes during previous discussion.

I added a new variable target_prob to save it in direct edge when calling
make_speculative, since the target_prob is assigned in ipa-profile, so no
need to stream out and in through LTO like speculative_id.

Even though, scaling in ipa-icf with multiple direct edges is more complicate then
single direct edge, need double circulation to find each src to dst edge map and do
scaling one by one. 

BTW, the breaking patch r279373 "Fix merging of common traget info." only mentioned
firefox but doesn't include a testcase, I tried to construct some test
cases, but fail to cover all the circumstance (I just implemented the N:N map yet, 
please refer to the code in below patch.), do you have cases other than firefox to
cover it?  Or how to test it as r279373 required?  As this piece of code is not quite
related to this patch, can we just leave it for future refine?  Thanks :)

Other changes are pasted as below patch. Thank you!


From: Xiong Hu Luo <luoxhu@linux.ibm.com>

v7:
 1. Remove GTY for summaries, replace ggc vec with auto_vec, remove
    is_empty.  Add comment for speculative_id.
 2. Create and duplicate all speculative direct edge's call summary
    in ipa-fnsummary.c.
 3. ipa_merge_profiles update with target_prob.  target_prob is
    generated in ipa-profile, so no need stream out and in through LTO.
 4. Bootstrap passed and no SPEC2017 regression on power8le.

This patch aims to fix PR69678 caused by PGO indirect call profiling
performance issues.
The bug that profiling data is never working was fixed by Martin's pull
back of topN patches, performance got GEOMEAN ~1% improvement(+24% for
511.povray_r specifically).
Still, currently the default profile only generates SINGLE indirect target
that called more than 75%.  This patch leverages MULTIPLE indirect
targets use in LTO-WPA and LTO-LTRANS stage, as a result, function
specialization, profiling, partial devirtualization, inlining and
cloning could be done successfully based on it.
Performance can get improved from 0.70 sec to 0.38 sec on simple tests.
Details are:
  1.  PGO with topn is enabled by default now, but only one indirect
  target edge will be generated in ipa-profile pass, so add variables to enable
  multiple speculative edges through passes, speculative_id will record the
  direct edge index bind to the indirect edge, indirect_call_targets length
  records how many direct edges owned by the indirect edge, postpone gimple_ic
  to ipa-profile like default as inline pass will decide whether it is benefit
  to transform indirect call.
  2.  Use speculative_id to track and search the reference node matched
  with the direct edge's callee for multiple targets.  Actually, it is the
  caller's responsibility to handle the direct edges mapped to same indirect
  edge.  speculative_call_info will return one of the direct edge specified,
  this will leverage current IPA edge process framework mostly.
  3.  Enable LTO WPA/LTRANS stage multiple indirect call targets analysis for
  profile full support in ipa passes and cgraph_edge functions.  speculative_id
  can be set by make_speculative id when multiple targets are binded to
  one indirect edge, and cloned if new edge is cloned.  speculative_id
  is streamed out and stream int by lto like lto_stmt_uid.
  4.  Add 1 in module testcase and 2 cross module testcases.
  5.  Bootstrap and regression test passed on Power8-LE.  No function
  and performance regression for SPEC2017.

gcc/ChangeLog

	2019-12-26  Xiong Hu Luo  <luoxhu@linux.ibm.com>

	PR ipa/69678
	* cgraph.c (symbol_table::create_edge): Init speculative_id and
	target_prob.
	(cgraph_edge::make_speculative): Add param for setting speculative_id
	and target_prob.
	(cgraph_edge::speculative_call_info): Update comments and find reference
	by speculative_id for multiple indirect targets.
	(cgraph_edge::resolve_speculation): Decrease the speculations
	for indirect edge, drop it's speculative if not direct target
	left. Update comments.
	(cgraph_edge::redirect_call_stmt_to_callee): Likewise.
	(cgraph_node::dump): Print num_speculative_call_targets.
	(cgraph_node::verify_node): Don't report error if speculative
	edge not include statement.
	(cgraph_edge::num_speculative_call_targets_p): New function.
	* cgraph.h (int common_target_id): Remove.
	(int common_target_probability): Remove.
	(num_speculative_call_targets): New variable.
	(make_speculative): Add param for setting speculative_id.
	(cgraph_edge::num_speculative_call_targets_p): New declare.
	(target_prob): New variable.
	(speculative_id): New variable.
	* ipa-fnsummary.c (analyze_function_body): Create and duplicate
	  call summaries for multiple speculative call targets.
	* cgraphclones.c (cgraph_node::create_clone): Clone speculative_id.
	* ipa-profile.c (struct speculative_call_target): New struct.
	(class speculative_call_summary): New class.
	(class speculative_call_summaries): New class.
	(call_sums): New variable.
	(ipa_profile_generate_summary): Generate indirect multiple targets summaries.
	(ipa_profile_write_edge_summary): New function.
	(ipa_profile_write_summary): Stream out indirect multiple targets summaries.
	(ipa_profile_dump_all_summaries): New function.
	(ipa_profile_read_edge_summary): New function.
	(ipa_profile_read_summary_section): New function.
	(ipa_profile_read_summary): Stream in indirect multiple targets summaries.
	(ipa_profile): Generate num_speculative_call_targets from
	profile summaries.
	* ipa-ref.h (speculative_id): New variable.
	* ipa-utils.c (ipa_merge_profiles): Update with target_prob.
	* lto-cgraph.c (lto_output_edge): Remove indirect common_target_id and
	common_target_probability.   Stream out speculative_id and
	num_speculative_call_targets.
	(input_edge): Likewise.
	* predict.c (dump_prediction): Remove edges count assert to be
	precise.
	* symtab.c (symtab_node::create_reference): Init speculative_id.
	(symtab_node::clone_references): Clone speculative_id.
	(symtab_node::clone_referring): Clone speculative_id.
	(symtab_node::clone_reference): Clone speculative_id.
	(symtab_node::clear_stmts_in_references): Clear speculative_id.
	* tree-inline.c (copy_bb): Duplicate all the speculative edges
	if indirect call contains multiple speculative targets.
	* value-prof.h  (check_ic_target): Remove.
	* value-prof.c  (gimple_value_profile_transformations):
	Use void function gimple_ic_transform.
	* value-prof.c  (gimple_ic_transform): Handle topn case.
	Fix comment typos.  Change it to a void function.

gcc/testsuite/ChangeLog

	2019-12-26  Xiong Hu Luo  <luoxhu@linux.ibm.com>

	PR ipa/69678
	* gcc.dg/tree-prof/indir-call-prof-topn.c: New testcase.
	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c: New testcase.
	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c: New testcase.
	* gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c: New testcase.
	* lib/scandump.exp: Dump executable file name.
	* lib/scanwpaipa.exp: New scan-pgo-wap-ipa-dump.
---
 gcc/cgraph.c                                  | 113 +++++-
 gcc/cgraph.h                                  |  27 +-
 gcc/cgraphclones.c                            |   1 +
 gcc/ipa-fnsummary.c                           |  22 +-
 gcc/ipa-profile.c                             | 357 ++++++++++++++++--
 gcc/ipa-ref.h                                 |   3 +
 gcc/ipa-utils.c                               | 173 ++++++---
 gcc/lto-cgraph.c                              |  30 +-
 gcc/predict.c                                 |   1 -
 gcc/symtab.c                                  |   5 +
 .../tree-prof/crossmodule-indir-call-topn-1.c |  33 ++
 .../crossmodule-indir-call-topn-1a.c          |  22 ++
 .../tree-prof/crossmodule-indir-call-topn-2.c |  40 ++
 .../gcc.dg/tree-prof/indir-call-prof-topn.c   |  37 ++
 gcc/testsuite/lib/scandump.exp                |   1 +
 gcc/testsuite/lib/scanwpaipa.exp              |  23 ++
 gcc/tree-inline.c                             |  32 +-
 gcc/value-prof.c                              |  87 +++--
 gcc/value-prof.h                              |   1 -
 19 files changed, 843 insertions(+), 165 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c

diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index 5ba33a5a0cc..2f64f782a17 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -857,6 +857,8 @@ symbol_table::create_edge (cgraph_node *caller, cgraph_node *callee,
   edge->prev_callee = NULL;
   edge->next_callee = NULL;
   edge->lto_stmt_uid = 0;
+  edge->target_prob = 0;
+  edge->speculative_id = 0;
 
   edge->count = count;
   edge->call_stmt = call_stmt;
@@ -1043,10 +1045,16 @@ cgraph_edge::remove (void)
    the reference representing the if conditional and attaches
    them all to the original indirect call statement.  
 
+   speculative_id is used to link direct calls with their corresponding
+   IPA_REF_ADDR references when representing speculative calls.
+
+   target_prob is the probability of the speculative call.
+
    Return direct edge created.  */
 
 cgraph_edge *
-cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
+cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count,
+			       unsigned int speculative_id, int target_prob)
 {
   cgraph_node *n = caller;
   ipa_ref *ref = NULL;
@@ -1064,24 +1072,53 @@ cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
   else
     e2->can_throw_external = can_throw_external;
   e2->lto_stmt_uid = lto_stmt_uid;
+  e2->speculative_id = speculative_id;
+  e2->target_prob = target_prob;
   e2->in_polymorphic_cdtor = in_polymorphic_cdtor;
   count -= e2->count;
   symtab->call_edge_duplication_hooks (this, e2);
   ref = n->create_reference (n2, IPA_REF_ADDR, call_stmt);
   ref->lto_stmt_uid = lto_stmt_uid;
+  ref->speculative_id = speculative_id;
   ref->speculative = speculative;
   n2->mark_address_taken ();
   return e2;
 }
 
-/* Speculative call consist of three components:
-   1) an indirect edge representing the original call
-   2) an direct edge representing the new call
-   3) ADDR_EXPR reference representing the speculative check.
-   All three components are attached to single statement (the indirect
-   call) and if one of them exists, all of them must exist.
+/* Speculative calls represent a transformation of indirect calls
+   which may be later inserted into gimple in the following form:
+
+   if (call_dest == target1)
+   target1 ();
+   else if (call_dest == target2)
+   target2 ();
+   else
+   call_dest ();
+
+   This is a win in the case when target1 and target2 are common values for
+   call_dest as determined by ipa-devirt or indirect call profiling.
+   In particular this may enable inlining and other optimizations.
+
+   Speculative call consists of the following main components:
+
+   1) One or more "speculative" direct call (num_speculative_call_targets is
+   speculative direct call count belongs to the speculative indirect call)
+   2) One or more IPA_REF_ADDR references (representing the fact that code above
+   takes address of target1 and target2)
+   3) The fallback "speculative" indirect call
 
-   Given speculative call edge, return all three components.
+   Direct calls and corresponding references are linked by
+   speculative_id.
+
+   speculative_call_info returns triple
+   (direct_call, indirect call, IPA_REF_ADDR reference)
+   when called on one edge participating in the speculative call:
+
+   1) If called on direct call, its corresponding IPA_REF_ADDR and related
+   indirect call are returned.
+
+   2) If called on indirect call, it will return one of direct edges and its
+   matching IPA_REF_ADDR.
  */
 
 void
@@ -1121,7 +1158,7 @@ cgraph_edge::speculative_call_info (cgraph_edge *&direct,
 
   reference = NULL;
   for (i = 0; e->caller->iterate_reference (i, ref); i++)
-    if (ref->speculative
+    if (ref->speculative && ref->speculative_id == e->speculative_id
 	&& ((ref->stmt && ref->stmt == e->call_stmt)
 	    || (!ref->stmt && ref->lto_stmt_uid == e->lto_stmt_uid)))
       {
@@ -1137,7 +1174,16 @@ cgraph_edge::speculative_call_info (cgraph_edge *&direct,
 
 /* Speculative call edge turned out to be direct call to CALLEE_DECL.
    Remove the speculative call sequence and return edge representing the call.
-   It is up to caller to redirect the call as appropriate. */
+
+   For "speculative" indirect call that contains multiple "speculative"
+   targets (i.e. edge->indirect_info->num_speculative_call_targets > 1),
+   decrease the count and only remove current direct edge.
+
+   If no speculative direct call left to the speculative indirect call, remove
+   the speculative of both the indirect call and corresponding direct edge.
+
+   It is up to caller to iteratively resolve each "speculative" direct call and
+   redirect the call as appropriate.  */
 
 cgraph_edge *
 cgraph_edge::resolve_speculation (tree callee_decl)
@@ -1182,7 +1228,16 @@ cgraph_edge::resolve_speculation (tree callee_decl)
          in the functions inlined through it.  */
     }
   edge->count += e2->count;
-  edge->speculative = false;
+  if (edge->num_speculative_call_targets_p ())
+    {
+      /* The indirect edge has multiple speculative targets, don't remove
+	 speculative until all related direct edges are resolved.  */
+      edge->indirect_info->num_speculative_call_targets--;
+      if (!edge->indirect_info->num_speculative_call_targets)
+	edge->speculative = false;
+    }
+  else
+    edge->speculative = false;
   e2->speculative = false;
   ref->remove_reference ();
   if (e2->indirect_unknown_callee || e2->inline_failed)
@@ -1242,7 +1297,17 @@ cgraph_edge::make_direct (cgraph_node *callee)
 }
 
 /* If necessary, change the function declaration in the call statement
-   associated with E so that it corresponds to the edge callee.  */
+   associated with E so that it corresponds to the edge callee.
+
+   The edge could be one of speculative direct call generated from speculative
+   indirect call.  In this circumstance, decrease the speculative targets
+   count (i.e. num_speculative_call_targets) and redirect call stmt to the
+   corresponding i-th target.  If no speculative direct call left to the
+   speculative indirect call, remove "speculative" of the indirect call and
+   also redirect stmt to it's final direct target.
+
+   It is up to caller to iteratively transform each "speculative"
+   direct call as appropriate.  */
 
 gimple *
 cgraph_edge::redirect_call_stmt_to_callee (void)
@@ -1290,7 +1355,17 @@ cgraph_edge::redirect_call_stmt_to_callee (void)
 	  e->caller->set_call_stmt_including_clones (e->call_stmt, new_stmt,
 						     false);
 	  e->count = gimple_bb (e->call_stmt)->count;
-	  e2->speculative = false;
+	  if (e2->num_speculative_call_targets_p ())
+	    {
+	      /* The indirect edge has multiple speculative targets, don't
+		 remove speculative until all related direct edges are
+		 redirected.  */
+	      e2->indirect_info->num_speculative_call_targets--;
+	      if (!e2->indirect_info->num_speculative_call_targets)
+		e2->speculative = false;
+	    }
+	  else
+	    e2->speculative = false;
 	  e2->count = gimple_bb (e2->call_stmt)->count;
 	  ref->speculative = false;
 	  ref->stmt = NULL;
@@ -2102,6 +2177,8 @@ cgraph_node::dump (FILE *f)
 	  if (edge->indirect_info->vptr_changed)
 	    fprintf (f, " (vptr maybe changed)");
 	}
+      fprintf (f, " Num speculative call targets: %i",
+	       edge->indirect_info->num_speculative_call_targets);
       fprintf (f, "\n");
       if (edge->indirect_info->polymorphic)
 	edge->indirect_info->context.dump (f);
@@ -3392,7 +3469,7 @@ cgraph_node::verify_node (void)
 
       for (e = callees; e; e = e->next_callee)
 	{
-	  if (!e->aux)
+	  if (!e->aux && !e->speculative)
 	    {
 	      error ("edge %s->%s has no corresponding call_stmt",
 		     identifier_to_locale (e->caller->name ()),
@@ -3731,6 +3808,14 @@ cgraph_edge::possibly_call_in_translation_unit_p (void)
   return node->get_availability () >= AVAIL_INTERPOSABLE;
 }
 
+/* Return num_speculative_targets of this edge.  */
+
+int
+cgraph_edge::num_speculative_call_targets_p (void)
+{
+  return indirect_info ? indirect_info->num_speculative_call_targets : 0;
+}
+
 /* A stashed copy of "symtab" for use by selftest::symbol_table_test.
    This needs to be a global so that it can be a GC root, and thus
    prevent the stashed copy from being garbage-collected if the GC runs
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index e96cb51d803..4678d9baeb8 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -1703,10 +1703,9 @@ public:
   int param_index;
   /* ECF flags determined from the caller.  */
   int ecf_flags;
-  /* Profile_id of common target obtained from profile.  */
-  int common_target_id;
-  /* Probability that call will land in function with COMMON_TARGET_ID.  */
-  int common_target_probability;
+
+  /* Number of speculative call targets, it's less than GCOV_TOPN_VALUES.  */
+  unsigned num_speculative_call_targets : 16;
 
   /* Set when the call is a virtual call with the parameter being the
      associated object pointer rather than a simple direct call.  */
@@ -1762,10 +1761,14 @@ public:
 
   /* Turn edge into speculative call calling N2. Update
      the profile so the direct call is taken COUNT times
-     with FREQUENCY.  */
-  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count);
-
-   /* Given speculative call edge, return all three components.  */
+     with FREQUENCY.  speculative_id is used to link direct calls with their
+     corresponding IPA_REF_ADDR references when representing speculative calls.
+     target_prob is the probability of the speculative call.  */
+  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count,
+				 unsigned int speculative_id = 0,
+				 int target_prob = 0);
+
+  /* Given speculative call edge, return all three components.  */
   void speculative_call_info (cgraph_edge *&direct, cgraph_edge *&indirect,
 			      ipa_ref *&reference);
 
@@ -1822,6 +1825,9 @@ public:
      be internal to the current translation unit.  */
   bool possibly_call_in_translation_unit_p (void);
 
+  /* Return num_speculative_targets of this edge.  */
+  int num_speculative_call_targets_p (void);
+
   /* Expected number of executions: calculated in profile.c.  */
   profile_count count;
   cgraph_node *caller;
@@ -1841,6 +1847,11 @@ public:
   /* The stmt_uid of call_stmt.  This is used by LTO to recover the call_stmt
      when the function is serialized in.  */
   unsigned int lto_stmt_uid;
+  /*  target_prob is the probability of the speculative call.  */
+  unsigned int target_prob;
+  /* speculative id is used to link direct calls with their corresponding
+     IPA_REF_ADDR references when representing speculative calls.  */
+  unsigned int speculative_id : 16;
   /* Whether this edge was made direct by indirect inlining.  */
   unsigned int indirect_inlining_edge : 1;
   /* Whether this edge describes an indirect call with an undetermined
diff --git a/gcc/cgraphclones.c b/gcc/cgraphclones.c
index dbdeb4510b4..21ff0a2687d 100644
--- a/gcc/cgraphclones.c
+++ b/gcc/cgraphclones.c
@@ -133,6 +133,7 @@ cgraph_edge::clone (cgraph_node *n, gcall *call_stmt, unsigned stmt_uid,
   new_edge->inline_failed = inline_failed;
   new_edge->indirect_inlining_edge = indirect_inlining_edge;
   new_edge->lto_stmt_uid = stmt_uid;
+  new_edge->speculative_id = speculative_id;
   /* Clone flags that depend on call_stmt availability manually.  */
   new_edge->can_throw_external = can_throw_external;
   new_edge->call_stmt_cannot_inline_p = call_stmt_cannot_inline_p;
diff --git a/gcc/ipa-fnsummary.c b/gcc/ipa-fnsummary.c
index 5486f6496bd..83933900706 100644
--- a/gcc/ipa-fnsummary.c
+++ b/gcc/ipa-fnsummary.c
@@ -2603,7 +2603,7 @@ analyze_function_body (struct cgraph_node *node, bool early)
 	      edge_set_predicate (edge, &bb_predicate);
 	      if (edge->speculative)
 		{
-		  cgraph_edge *direct, *indirect;
+		  cgraph_edge *direct, *indirect, *next_direct;
 		  ipa_ref *ref;
 		  edge->speculative_call_info (direct, indirect, ref);
 		  gcc_assert (direct == edge);
@@ -2611,6 +2611,26 @@ analyze_function_body (struct cgraph_node *node, bool early)
 			 = ipa_call_summaries->get_create (indirect);
 		  ipa_call_summaries->duplicate (edge, indirect,
 						 es, es2);
+
+		  /* Create and duplicate call summaries for multiple
+		     speculative call targets.  */
+		  int num_specs = indirect->num_speculative_call_targets_p ();
+		  if (num_specs > 1)
+		    for (next_direct = edge->next_callee;
+			 next_direct && --num_specs;
+			 next_direct = next_direct->next_callee)
+		      {
+			next_direct->speculative_call_info (direct, indirect,
+							    ref);
+			if (direct == next_direct && next_direct->speculative
+			    && edge->call_stmt == stmt)
+			  {
+			    ipa_call_summary *es3
+			      = ipa_call_summaries->get_create (next_direct);
+			    ipa_call_summaries->duplicate (edge, next_direct,
+							   es, es3);
+			  }
+		      }
 		}
 	    }
 
diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
index 4b28b94aaad..20eba2c6953 100644
--- a/gcc/ipa-profile.c
+++ b/gcc/ipa-profile.c
@@ -159,7 +159,99 @@ dump_histogram (FILE *file, vec<histogram_entry *> histogram)
    }
 }
 
-/* Collect histogram from CFG profiles.  */
+/* Structure containing speculative target information from profile.  */
+
+struct speculative_call_target
+{
+  speculative_call_target (unsigned int id = 0, int prob = 0)
+    : target_id (id), target_probability (prob)
+  {
+  }
+
+  /* Profile_id of target obtained from profile.  */
+  unsigned int target_id;
+  /* Probability that call will land in function with target_id.  */
+  unsigned int target_probability;
+};
+
+class speculative_call_summary
+{
+public:
+  speculative_call_summary () : speculative_call_targets ()
+  {}
+
+  auto_vec<speculative_call_target> speculative_call_targets;
+
+  void dump (FILE *f);
+
+};
+
+  /* Class to manage call summaries.  */
+
+class ipa_profile_call_summaries
+  : public call_summary<speculative_call_summary *>
+{
+public:
+  ipa_profile_call_summaries (symbol_table *table)
+    : call_summary<speculative_call_summary *> (table)
+  {}
+
+  /* Duplicate info when an edge is cloned.  */
+  virtual void duplicate (cgraph_edge *, cgraph_edge *,
+			  speculative_call_summary *old_sum,
+			  speculative_call_summary *new_sum);
+};
+
+static ipa_profile_call_summaries *call_sums = NULL;
+
+/* Dump all information in speculative call summary to F.  */
+
+void
+speculative_call_summary::dump (FILE *f)
+{
+  cgraph_node *n2;
+
+  unsigned spec_count = speculative_call_targets.length ();
+  for (unsigned i = 0; i < spec_count; i++)
+    {
+      speculative_call_target item = speculative_call_targets[i];
+      n2 = find_func_by_profile_id (item.target_id);
+      if (n2)
+	fprintf (f, "    The %i speculative target is %s with prob %3.2f\n", i,
+		 n2->dump_name (),
+		 item.target_probability / (float) REG_BR_PROB_BASE);
+      else
+	fprintf (f, "    The %i speculative target is %u with prob %3.2f\n", i,
+		 item.target_id,
+		 item.target_probability / (float) REG_BR_PROB_BASE);
+    }
+}
+
+/* Duplicate info when an edge is cloned.  */
+
+void
+ipa_profile_call_summaries::duplicate (cgraph_edge *, cgraph_edge *,
+				       speculative_call_summary *old_sum,
+				       speculative_call_summary *new_sum)
+{
+  if (!old_sum)
+    return;
+
+  unsigned old_count = old_sum->speculative_call_targets.length ();
+  if (!old_count)
+    return;
+
+  new_sum->speculative_call_targets.reserve_exact (old_count);
+  new_sum->speculative_call_targets.quick_grow_cleared (old_count);
+
+  for (unsigned i = 0; i < old_count; i++)
+    {
+      new_sum->speculative_call_targets[i]
+	= old_sum->speculative_call_targets[i];
+    }
+}
+
+/* Collect histogram and speculative target summaries from CFG profiles.  */
 
 static void
 ipa_profile_generate_summary (void)
@@ -169,7 +261,10 @@ ipa_profile_generate_summary (void)
   basic_block bb;
 
   hash_table<histogram_hash> hashtable (10);
-  
+
+  gcc_checking_assert (!call_sums);
+  call_sums = new ipa_profile_call_summaries (symtab);
+
   FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
     if (ENTRY_BLOCK_PTR_FOR_FN (DECL_STRUCT_FUNCTION (node->decl))->count.ipa_p ())
       FOR_EACH_BB_FN (bb, DECL_STRUCT_FUNCTION (node->decl))
@@ -191,23 +286,35 @@ ipa_profile_generate_summary (void)
 		  if (h)
 		    {
 		      gcov_type val, count, all;
-		      if (get_nth_most_common_value (NULL, "indirect call", h,
-						     &val, &count, &all))
+		      struct cgraph_edge *e = node->get_edge (stmt);
+		      if (e && !e->indirect_unknown_callee)
+			continue;
+
+		      speculative_call_summary *csum
+			= call_sums->get_create (e);
+
+		      for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
 			{
-			  struct cgraph_edge * e = node->get_edge (stmt);
-			  if (e && !e->indirect_unknown_callee)
+			  if (!get_nth_most_common_value (NULL, "indirect call",
+							  h, &val, &count, &all,
+							  j))
+			    continue;
+
+			  if (val == 0)
 			    continue;
 
-			  e->indirect_info->common_target_id = val;
-			  e->indirect_info->common_target_probability
-			    = GCOV_COMPUTE_SCALE (count, all);
-			  if (e->indirect_info->common_target_probability > REG_BR_PROB_BASE)
+			  speculative_call_target item (
+			    val, GCOV_COMPUTE_SCALE (count, all));
+			  if (item.target_probability > REG_BR_PROB_BASE)
 			    {
 			      if (dump_file)
-				fprintf (dump_file, "Probability capped to 1\n");
-			      e->indirect_info->common_target_probability = REG_BR_PROB_BASE;
+				fprintf (dump_file,
+					 "Probability capped to 1\n");
+			      item.target_probability = REG_BR_PROB_BASE;
 			    }
+			  csum->speculative_call_targets.safe_push (item);
 			}
+
 		      gimple_remove_histogram_value (DECL_STRUCT_FUNCTION (node->decl),
 						      stmt, h);
 		    }
@@ -222,6 +329,33 @@ ipa_profile_generate_summary (void)
   histogram.qsort (cmp_counts);
 }
 
+/* Serialize the speculative summary info for LTO.  */
+
+static void
+ipa_profile_write_edge_summary (lto_simple_output_block *ob,
+				speculative_call_summary *csum)
+{
+  unsigned len = 0;
+
+  len = csum->speculative_call_targets.length ();
+
+  gcc_assert (len <= GCOV_TOPN_VALUES);
+
+  streamer_write_hwi_stream (ob->main_stream, len);
+
+  if (len)
+    {
+      unsigned spec_count = csum->speculative_call_targets.length ();
+      for (unsigned i = 0; i < spec_count; i++)
+	{
+	  speculative_call_target item = csum->speculative_call_targets[i];
+	  gcc_assert (item.target_id);
+	  streamer_write_hwi_stream (ob->main_stream, item.target_id);
+	  streamer_write_hwi_stream (ob->main_stream, item.target_probability);
+	}
+    }
+}
+
 /* Serialize the ipa info for lto.  */
 
 static void
@@ -238,10 +372,122 @@ ipa_profile_write_summary (void)
       streamer_write_uhwi_stream (ob->main_stream, histogram[i]->time);
       streamer_write_uhwi_stream (ob->main_stream, histogram[i]->size);
     }
+
+  if (!call_sums)
+    return;
+
+  /* Serialize speculative targets information.  */
+  unsigned int count = 0;
+  lto_symtab_encoder_t encoder = ob->decl_state->symtab_node_encoder;
+  lto_symtab_encoder_iterator lsei;
+  cgraph_node *node;
+
+  for (lsei = lsei_start_function_in_partition (encoder); !lsei_end_p (lsei);
+       lsei_next_function_in_partition (&lsei))
+    {
+      node = lsei_cgraph_node (lsei);
+      if (node->definition && node->has_gimple_body_p ()
+	  && node->indirect_calls)
+	count++;
+    }
+
+  streamer_write_uhwi_stream (ob->main_stream, count);
+
+  /* Process all of the functions.  */
+  for (lsei = lsei_start_function_in_partition (encoder);
+       !lsei_end_p (lsei) && count; lsei_next_function_in_partition (&lsei))
+    {
+      cgraph_node *node = lsei_cgraph_node (lsei);
+      if (node->definition && node->has_gimple_body_p ()
+	  && node->indirect_calls)
+	{
+	  int node_ref = lto_symtab_encoder_encode (encoder, node);
+	  streamer_write_uhwi_stream (ob->main_stream, node_ref);
+
+	  for (cgraph_edge *e = node->indirect_calls; e; e = e->next_callee)
+	    {
+	      speculative_call_summary *csum = call_sums->get_create (e);
+	      ipa_profile_write_edge_summary (ob, csum);
+	    }
+      }
+    }
+
   lto_destroy_simple_output_block (ob);
 }
 
-/* Deserialize the ipa info for lto.  */
+/* Dump all profile summary data for all cgraph nodes and edges to file F.  */
+
+static void
+ipa_profile_dump_all_summaries (FILE *f)
+{
+  fprintf (dump_file,
+	   "\n========== IPA-profile speculative targets: ==========\n");
+  cgraph_node *node;
+  FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
+    {
+      fprintf (f, "\nSummary for node %s:\n", node->dump_name ());
+      for (cgraph_edge *e = node->indirect_calls; e; e = e->next_callee)
+	{
+	  fprintf (f, "  Summary for %s of indirect edge %d:\n",
+		   e->caller->dump_name (), e->lto_stmt_uid);
+	  speculative_call_summary *csum = call_sums->get_create (e);
+	  csum->dump (f);
+	}
+    }
+  fprintf (f, "\n\n");
+}
+
+/* Read speculative targets information about edge for LTO WPA.  */
+
+static void
+ipa_profile_read_edge_summary (class lto_input_block *ib, cgraph_edge *edge)
+{
+  unsigned i, len;
+
+  len = streamer_read_hwi (ib);
+  gcc_assert (len <= GCOV_TOPN_VALUES);
+
+  speculative_call_summary *csum = call_sums->get_create (edge);
+
+  for (i = 0; i < len; i++)
+  {
+    speculative_call_target item (streamer_read_hwi (ib),
+	streamer_read_hwi (ib));
+    csum->speculative_call_targets.safe_push (item);
+  }
+}
+
+/* Read profile speculative targets section information for LTO WPA.  */
+
+static void
+ipa_profile_read_summary_section (struct lto_file_decl_data *file_data,
+				  class lto_input_block *ib)
+{
+  if (!ib)
+    return;
+
+  lto_symtab_encoder_t encoder = file_data->symtab_node_encoder;
+
+  unsigned int count = streamer_read_uhwi (ib);
+
+  unsigned int i;
+  unsigned int index;
+  cgraph_node * node;
+
+  for (i = 0; i < count; i++)
+    {
+      index = streamer_read_uhwi (ib);
+      encoder = file_data->symtab_node_encoder;
+      node
+	= dyn_cast<cgraph_node *> (lto_symtab_encoder_deref (encoder, index));
+
+      for (cgraph_edge *e = node->indirect_calls; e; e = e->next_callee)
+	ipa_profile_read_edge_summary (ib, e);
+    }
+}
+
+/* Deserialize the IPA histogram and speculative targets summary info for LTO.
+   */
 
 static void
 ipa_profile_read_summary (void)
@@ -253,6 +499,9 @@ ipa_profile_read_summary (void)
 
   hash_table<histogram_hash> hashtable (10);
 
+  gcc_checking_assert (!call_sums);
+  call_sums = new ipa_profile_call_summaries (symtab);
+
   while ((file_data = file_data_vec[j++]))
     {
       const char *data;
@@ -273,6 +522,9 @@ ipa_profile_read_summary (void)
 	      account_time_size (&hashtable, histogram,
 				 count, time, size);
 	    }
+
+	  ipa_profile_read_summary_section (file_data, ib);
+
 	  lto_destroy_simple_input_block (file_data,
 					  LTO_section_ipa_profile,
 					  ib, data, len);
@@ -512,6 +764,7 @@ ipa_profile (void)
   int nindirect = 0, ncommon = 0, nunknown = 0, nuseless = 0, nconverted = 0;
   int nmismatch = 0, nimpossible = 0;
   bool node_map_initialized = false;
+  gcov_type threshold;
 
   if (dump_file)
     dump_histogram (dump_file, histogram);
@@ -520,14 +773,12 @@ ipa_profile (void)
       overall_time += histogram[i]->count * histogram[i]->time;
       overall_size += histogram[i]->size;
     }
+  threshold = 0;
   if (overall_time)
     {
-      gcov_type threshold;
-
       gcc_assert (overall_size);
 
       cutoff = (overall_time * param_hot_bb_count_ws_permille + 500) / 1000;
-      threshold = 0;
       for (i = 0; cumulated < cutoff; i++)
 	{
 	  cumulated += histogram[i]->count * histogram[i]->time;
@@ -563,10 +814,21 @@ ipa_profile (void)
   histogram.release ();
   histogram_pool.release ();
 
-  /* Produce speculative calls: we saved common target from porfiling into
-     e->common_target_id.  Now, at link time, we can look up corresponding
+  /* Produce speculative calls: we saved common target from profiling into
+     e->target_id.  Now, at link time, we can look up corresponding
      function node and produce speculative call.  */
 
+  gcc_checking_assert (call_sums);
+
+  if (dump_file)
+    {
+      if (!node_map_initialized)
+	init_node_map (false);
+      node_map_initialized = true;
+
+      ipa_profile_dump_all_summaries (dump_file);
+    }
+
   FOR_EACH_DEFINED_FUNCTION (n)
     {
       bool update = false;
@@ -578,13 +840,39 @@ ipa_profile (void)
 	{
 	  if (n->count.initialized_p ())
 	    nindirect++;
-	  if (e->indirect_info->common_target_id)
+
+	  speculative_call_summary *csum = call_sums->get_create (e);
+	  unsigned spec_count = csum->speculative_call_targets.length ();
+	  if (spec_count)
 	    {
 	      if (!node_map_initialized)
-	        init_node_map (false);
+		init_node_map (false);
 	      node_map_initialized = true;
 	      ncommon++;
-	      n2 = find_func_by_profile_id (e->indirect_info->common_target_id);
+
+	      if (in_lto_p)
+		{
+		  if (dump_file)
+		    {
+		      fprintf (dump_file,
+			       "Updating hotness threshold in LTO mode.\n");
+		      fprintf (dump_file, "Updated min count: %" PRId64 "\n",
+			       (int64_t) threshold / spec_count);
+		    }
+		  set_hot_bb_threshold (threshold / spec_count);
+		}
+
+	      unsigned speculative_id = 0;
+	      bool speculative_found = false;
+	      /* The code below is not formatted yet for review convenience.
+		 Move to a seprate small function is not easy as too many local
+		 variables used in it.  Need format and remove this comments
+		 once got approved.  */
+	      for (unsigned i = 0; i < spec_count; i++)
+	      {
+	      speculative_call_target item
+		= csum->speculative_call_targets[i];
+	      n2 = find_func_by_profile_id (item.target_id);
 	      if (n2)
 		{
 		  if (dump_file)
@@ -593,11 +881,10 @@ ipa_profile (void)
 			       " other module %s => %s, prob %3.2f\n",
 			       n->dump_name (),
 			       n2->dump_name (),
-			       e->indirect_info->common_target_probability
-			       / (float)REG_BR_PROB_BASE);
+			       item.target_probability
+				 / (float) REG_BR_PROB_BASE);
 		    }
-		  if (e->indirect_info->common_target_probability
-		      < REG_BR_PROB_BASE / 2)
+		  if (item.target_probability < REG_BR_PROB_BASE / 2)
 		    {
 		      nuseless++;
 		      if (dump_file)
@@ -653,20 +940,26 @@ ipa_profile (void)
 			    n2 = alias;
 			}
 		      nconverted++;
-		      e->make_speculative
-			(n2,
-			 e->count.apply_probability
-				     (e->indirect_info->common_target_probability));
+		      e->make_speculative (n2,
+					   e->count.apply_probability (
+					     item.target_probability),
+					   speculative_id,
+					   item.target_probability);
 		      update = true;
+		      speculative_id++;
+		      speculative_found = true;
 		    }
 		}
 	      else
 		{
 		  if (dump_file)
 		    fprintf (dump_file, "Function with profile-id %i not found.\n",
-			     e->indirect_info->common_target_id);
+			     item.target_id);
 		  nunknown++;
 		}
+	       }
+	     if (speculative_found)
+	       e->indirect_info->num_speculative_call_targets = speculative_id;
 	    }
 	 }
        if (update)
@@ -729,6 +1022,10 @@ ipa_profile (void)
 	}
     }
   free (order);
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    symtab->dump (dump_file);
+
   return 0;
 }
 
diff --git a/gcc/ipa-ref.h b/gcc/ipa-ref.h
index 00af24c77db..523f951d5cf 100644
--- a/gcc/ipa-ref.h
+++ b/gcc/ipa-ref.h
@@ -59,6 +59,9 @@ public:
   symtab_node *referred;
   gimple *stmt;
   unsigned int lto_stmt_uid;
+  /* speculative id is used to link direct calls with their corresponding
+     IPA_REF_ADDR references when representing speculative calls.  */
+  unsigned int speculative_id : 16;
   unsigned int referred_index;
   ENUM_BITFIELD (ipa_ref_use) use:3;
   unsigned int speculative:1;
diff --git a/gcc/ipa-utils.c b/gcc/ipa-utils.c
index cc65e2a7a00..a110c8856a0 100644
--- a/gcc/ipa-utils.c
+++ b/gcc/ipa-utils.c
@@ -675,68 +675,133 @@ ipa_merge_profiles (struct cgraph_node *dst,
 	   e2 = (e2 ? e2->next_callee : NULL), e = e->next_callee)
 	{
 	  profile_count count = gimple_bb (e->call_stmt)->count;
+	  /* Below code are introduced by r279373 of "Fix merging of common
+	     traget info.".
+
+	     ipa-icf runs after ipa-profile, common_target_id with
+	     common_target_probablity are useless in ipa-icf since they are
+	     moved from cgraph.h to ipa-profile.c and processed already.
+	     Need double circulation to find out each mapped direct speculative
+	     edge and do prob merge.  Not easy to construct a case to cover all
+	     circumstances here.  For src and dst both have multiple speculative
+	     targets, only N:N maps are implemented, 2:0, 2:1, 0:2, 1:2 are not
+	     implemented yet as too complicated and no test cases to cover.  */
 	  if (copy_counts)
 	    {
-	      e->indirect_info->common_target_id
-		      = e2->indirect_info->common_target_id;
-	      e->indirect_info->common_target_probability
-		      = e2->indirect_info->common_target_probability;
+	      /* copy if both e and e2 have same num_speculative_call_targets.
+	       */
+	      if (e->num_speculative_call_targets_p ()
+		  == e2->num_speculative_call_targets_p ())
+		{
+		  int num_specs = e->num_speculative_call_targets_p ();
+		  cgraph_edge *direct, *indirect, *next_direct;
+		  cgraph_edge *direct2, *indirect2, *next_direct2;
+		  ipa_ref *ref;
+		  for (next_direct = e; next_direct && num_specs--;
+		       next_direct = direct->next_callee)
+		    {
+		      next_direct->speculative_call_info (direct, indirect,
+							  ref);
+
+		      int num_specs2 = e2->num_speculative_call_targets_p ();
+		      for (next_direct2 = e2; next_direct2 && num_specs2--;
+			   next_direct2 = direct2->next_callee)
+			{
+			  if (e2 && e2->speculative)
+			    next_direct2->speculative_call_info (direct2,
+								 indirect2,
+								 ref);
+			  if (direct->speculative_id == direct2->speculative_id
+			      && direct->lto_stmt_uid == direct2->lto_stmt_uid)
+			    {
+			      direct->target_prob = direct2->target_prob;
+			      break;
+			    }
+			}
+		    }
+		}
+	      else
+		gcc_assert (e->num_speculative_call_targets_p ()
+			    && e->num_speculative_call_targets_p ());
 	    }
-	  else if (e->indirect_info->common_target_id
-		   || e2->indirect_info->common_target_id)
+	  else if (e->num_speculative_call_targets_p ()
+		   || e2->num_speculative_call_targets_p ())
 	    {
-	      sreal scale1
-		 = e->count.ipa().to_sreal_scale (count);
-	      sreal scale2
-		 = e2->count.ipa().to_sreal_scale (count);
-
-	      if (scale1 == 0 && scale2 == 0)
-		scale1 = scale2 = 1;
-	      sreal sum = scale1 + scale2;
-	      int scaled_probability1
-		      = ((sreal)e->indirect_info->common_target_probability
-			* scale1 / sum).to_int ();
-	      int scaled_probability2
-		      = ((sreal)e2->indirect_info->common_target_probability
-			 * scale2 / sum).to_int ();
-	      if (symtab->dump_file)
+	      if (e->num_speculative_call_targets_p ()
+		  == e2->num_speculative_call_targets_p ())
 		{
-		  fprintf (symtab->dump_file,
-			   "Merging common targets %i prob %i"
-			   " and %i prob %i with scales %f %f\n",
-			   e->indirect_info->common_target_id,
-			   e->indirect_info->common_target_probability,
-			   e2->indirect_info->common_target_id,
-			   e2->indirect_info->common_target_probability,
-			   scale1.to_double (),
-			   scale2.to_double ());
-		  fprintf (symtab->dump_file, "Combined BB count ");
-		  count.dump (symtab->dump_file);
-		  fprintf (symtab->dump_file, " dst edge count ");
-		  e->count.dump (symtab->dump_file);
-		  fprintf (symtab->dump_file, " src edge count ");
-		  e2->count.dump (symtab->dump_file);
-		  fprintf (symtab->dump_file, "\n");
+		  int num_specs = e->num_speculative_call_targets_p ();
+		  cgraph_edge *direct, *indirect, *next_direct;
+		  cgraph_edge *direct2, *indirect2, *next_direct2;
+		  ipa_ref *ref;
+		  for (next_direct = e; next_direct && num_specs--;
+		       next_direct = direct->next_callee)
+		    {
+		      next_direct->speculative_call_info (direct, indirect,
+							  ref);
+
+		      int num_specs2 = e2->num_speculative_call_targets_p ();
+		      for (next_direct2 = e2; next_direct2 && num_specs2--;
+			   next_direct2 = direct2->next_callee)
+			{
+			  if (e2 && e2->speculative)
+			    next_direct2->speculative_call_info (direct2,
+								 indirect2,
+								 ref);
+			  if (direct->speculative_id == direct2->speculative_id
+			      && direct->lto_stmt_uid == direct2->lto_stmt_uid)
+			    {
+			      sreal scale1
+				= e->count.ipa().to_sreal_scale (count);
+			      sreal scale2
+				= e2->count.ipa().to_sreal_scale (count);
+
+			      if (scale1 == 0 && scale2 == 0)
+				scale1 = scale2 = 1;
+			      sreal sum = scale1 + scale2;
+			      int scaled_prob1
+				= (((sreal)direct->target_prob)
+				   * scale1 / sum).to_int ();
+			      int scaled_prob2
+				= (((sreal)direct2->target_prob)
+				   * scale2 / sum).to_int ();
+			      if (symtab->dump_file)
+				{
+				  fprintf (
+				    symtab->dump_file,
+				    "Merging speculative id %i prob %i"
+				    " and %i prob %i with scales %f %f\n",
+				    direct->speculative_id, direct->target_prob,
+				    direct2->speculative_id,
+				    direct2->target_prob, scale1.to_double (),
+				    scale2.to_double ());
+				  fprintf (symtab->dump_file,
+					   "Combined BB count ");
+				  count.dump (symtab->dump_file);
+				  fprintf (symtab->dump_file,
+					   " dst edge count ");
+				  e->count.dump (symtab->dump_file);
+				  fprintf (symtab->dump_file,
+					   " src edge count ");
+				  e2->count.dump (symtab->dump_file);
+				  fprintf (symtab->dump_file, "\n");
+				}
+			      direct->target_prob = scaled_prob1 + scaled_prob2;
+			      break;
+			    }
+			}
+		    }
 		}
-	      if (e->indirect_info->common_target_id
-		  == e2->indirect_info->common_target_id)
-		e->indirect_info->common_target_probability
-		       	= scaled_probability1 + scaled_probability2;
-	      else if (!e2->indirect_info->common_target_id
-		       || scaled_probability1 > scaled_probability2)
-		e->indirect_info->common_target_probability
-		       	= scaled_probability1;
-	      else 
+	      else if (e->num_speculative_call_targets_p ())
 		{
-		  e->indirect_info->common_target_id
-			  = e2->indirect_info->common_target_id;
-		  e->indirect_info->common_target_probability
-			  = scaled_probability2;
+		  /* Process if only dst is speculative.  */
+		  gcc_assert (!e->num_speculative_call_targets_p ());
+		}
+	      else if (e2->num_speculative_call_targets_p ())
+		{
+		  /* Process if only src is speculative.  */
+		  gcc_assert (!e2->num_speculative_call_targets_p ());
 		}
-	      if (symtab->dump_file)
-		fprintf (symtab->dump_file, "Merged as %i prob %i\n",
-			 e->indirect_info->common_target_id,
-			 e->indirect_info->common_target_probability);
 	    }
 
 	  /* When call is speculative, we need to re-distribute probabilities
diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
index b5221cd41f9..454fba607dc 100644
--- a/gcc/lto-cgraph.c
+++ b/gcc/lto-cgraph.c
@@ -262,6 +262,7 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
   bp_pack_enum (&bp, cgraph_inline_failed_t,
 	        CIF_N_REASONS, edge->inline_failed);
   bp_pack_var_len_unsigned (&bp, uid);
+  bp_pack_value (&bp, edge->speculative_id, 16);
   bp_pack_value (&bp, edge->indirect_inlining_edge, 1);
   bp_pack_value (&bp, edge->speculative, 1);
   bp_pack_value (&bp, edge->call_stmt_cannot_inline_p, 1);
@@ -284,16 +285,11 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
 			     | ECF_SIBCALL
 			     | ECF_LEAF
 			     | ECF_NOVOPS)));
+
+      bp_pack_value (&bp, edge->indirect_info->num_speculative_call_targets,
+		     16);
     }
   streamer_write_bitpack (&bp);
-  if (edge->indirect_unknown_callee)
-    {
-      streamer_write_hwi_stream (ob->main_stream,
-			         edge->indirect_info->common_target_id);
-      if (edge->indirect_info->common_target_id)
-	streamer_write_hwi_stream
-	   (ob->main_stream, edge->indirect_info->common_target_probability);
-    }
 }
 
 /* Return if NODE contain references from other partitions.  */
@@ -690,6 +686,8 @@ lto_output_ref (struct lto_simple_output_block *ob, struct ipa_ref *ref,
       if (ref->stmt)
 	uid = gimple_uid (ref->stmt) + 1;
       streamer_write_hwi_stream (ob->main_stream, uid);
+      bp_pack_value (&bp, ref->speculative_id, 16);
+      streamer_write_bitpack (&bp);
     }
 }
 
@@ -1428,7 +1426,11 @@ input_ref (class lto_input_block *ib,
   ref = referring_node->create_reference (node, use);
   ref->speculative = speculative;
   if (is_a <cgraph_node *> (referring_node))
-    ref->lto_stmt_uid = streamer_read_hwi (ib);
+    {
+      ref->lto_stmt_uid = streamer_read_hwi (ib);
+      bp = streamer_read_bitpack (ib);
+      ref->speculative_id = bp_unpack_value (&bp, 16);
+    }
 }
 
 /* Read an edge from IB.  NODES points to a vector of previously read nodes for
@@ -1442,7 +1444,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
 {
   struct cgraph_node *caller, *callee;
   struct cgraph_edge *edge;
-  unsigned int stmt_id;
+  unsigned int stmt_id, speculative_id;
   profile_count count;
   cgraph_inline_failed_t inline_failed;
   struct bitpack_d bp;
@@ -1466,6 +1468,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
   bp = streamer_read_bitpack (ib);
   inline_failed = bp_unpack_enum (&bp, cgraph_inline_failed_t, CIF_N_REASONS);
   stmt_id = bp_unpack_var_len_unsigned (&bp);
+  speculative_id = bp_unpack_value (&bp, 16);
 
   if (indirect)
     edge = caller->create_indirect_edge (NULL, 0, count);
@@ -1475,6 +1478,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
   edge->indirect_inlining_edge = bp_unpack_value (&bp, 1);
   edge->speculative = bp_unpack_value (&bp, 1);
   edge->lto_stmt_uid = stmt_id;
+  edge->speculative_id = speculative_id;
   edge->inline_failed = inline_failed;
   edge->call_stmt_cannot_inline_p = bp_unpack_value (&bp, 1);
   edge->can_throw_external = bp_unpack_value (&bp, 1);
@@ -1494,9 +1498,9 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
       if (bp_unpack_value (&bp, 1))
 	ecf_flags |= ECF_RETURNS_TWICE;
       edge->indirect_info->ecf_flags = ecf_flags;
-      edge->indirect_info->common_target_id = streamer_read_hwi (ib);
-      if (edge->indirect_info->common_target_id)
-        edge->indirect_info->common_target_probability = streamer_read_hwi (ib);
+
+      edge->indirect_info->num_speculative_call_targets
+	= bp_unpack_value (&bp, 16);
     }
 }
 
diff --git a/gcc/predict.c b/gcc/predict.c
index 4b5b77e78d2..a5c0ebcb181 100644
--- a/gcc/predict.c
+++ b/gcc/predict.c
@@ -761,7 +761,6 @@ dump_prediction (FILE *file, enum br_predictor predictor, int probability,
       && bb->count.precise_p ()
       && reason == REASON_NONE)
     {
-      gcc_assert (e->count ().precise_p ());
       fprintf (file, ";;heuristics;%s;%" PRId64 ";%" PRId64 ";%.1f;\n",
 	       predictor_info[predictor].name,
 	       bb->count.to_gcov_type (), e->count ().to_gcov_type (),
diff --git a/gcc/symtab.c b/gcc/symtab.c
index 9c52192fced..b0a0d7fa0ef 100644
--- a/gcc/symtab.c
+++ b/gcc/symtab.c
@@ -605,6 +605,7 @@ symtab_node::create_reference (symtab_node *referred_node,
   ref->referred = referred_node;
   ref->stmt = stmt;
   ref->lto_stmt_uid = 0;
+  ref->speculative_id = 0;
   ref->use = use_type;
   ref->speculative = 0;
 
@@ -662,6 +663,7 @@ symtab_node::clone_references (symtab_node *node)
       ref2 = create_reference (ref->referred, ref->use, ref->stmt);
       ref2->speculative = speculative;
       ref2->lto_stmt_uid = stmt_uid;
+      ref2->speculative_id = ref->speculative_id;
     }
 }
 
@@ -680,6 +682,7 @@ symtab_node::clone_referring (symtab_node *node)
       ref2 = ref->referring->create_reference (this, ref->use, ref->stmt);
       ref2->speculative = speculative;
       ref2->lto_stmt_uid = stmt_uid;
+      ref2->speculative_id = ref->speculative_id;
     }
 }
 
@@ -695,6 +698,7 @@ symtab_node::clone_reference (ipa_ref *ref, gimple *stmt)
   ref2 = create_reference (ref->referred, ref->use, stmt);
   ref2->speculative = speculative;
   ref2->lto_stmt_uid = stmt_uid;
+  ref2->speculative_id = ref->speculative_id;
   return ref2;
 }
 
@@ -749,6 +753,7 @@ symtab_node::clear_stmts_in_references (void)
       {
 	r->stmt = NULL;
 	r->lto_stmt_uid = 0;
+	r->speculative_id = 0;
       }
 }
 
diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
new file mode 100644
index 00000000000..a13b08cd60e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
@@ -0,0 +1,33 @@
+/* { dg-require-effective-target lto } */
+/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate" } */
+
+#include <stdio.h>
+
+typedef int (*fptr) (int);
+int
+one (int a);
+
+int
+two (int a);
+
+fptr table[] = {&one, &two};
+
+int
+main()
+{
+  int i, x;
+  fptr p = &one;
+
+  x = one (3);
+
+  for (i = 0; i < 350000000; i++)
+    {
+      x = (*p) (3);
+      p = table[x];
+    }
+  printf ("done:%d\n", x);
+}
+
+/* { dg-final-use-not-autofdo { scan-pgo-wpa-ipa-dump "2 \\(200.00%\\) speculations produced." "profile_estimate" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
new file mode 100644
index 00000000000..a8c6e365fb9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
@@ -0,0 +1,22 @@
+/* It seems there is no way to avoid the other source of mulitple
+   source testcase from being compiled independently.  Just avoid
+   error.  */
+#ifdef DOJOB
+int
+one (int a)
+{
+  return 1;
+}
+
+int
+two (int a)
+{
+  return 0;
+}
+#else
+int
+main()
+{
+  return 0;
+}
+#endif
diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
new file mode 100644
index 00000000000..9b996fcf0ed
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
@@ -0,0 +1,40 @@
+/* { dg-require-effective-target lto } */
+/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate" } */
+
+#include <stdio.h>
+
+typedef int (*fptr) (int);
+int
+one (int a);
+
+int
+two (int a);
+
+fptr table[] = {&one, &two};
+
+int foo ()
+{
+  int i, x;
+  fptr p = &one;
+
+  x = one (3);
+
+  for (i = 0; i < 350000000; i++)
+    {
+      x = (*p) (3);
+      p = table[x];
+    }
+  return x;
+}
+
+int
+main()
+{
+  int x = foo ();
+  printf ("done:%d\n", x);
+}
+
+/* { dg-final-use-not-autofdo { scan-pgo-wpa-ipa-dump "2 \\(200.00%\\) speculations produced." "profile_estimate" } } */
+
diff --git a/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
new file mode 100644
index 00000000000..063996c71df
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
@@ -0,0 +1,37 @@
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-options "-O2 -fdump-ipa-profile_estimate" } */
+
+#include <stdio.h>
+
+typedef int (*fptr) (int);
+int
+one (int a)
+{
+  return 1;
+}
+
+int
+two (int a)
+{
+  return 0;
+}
+
+fptr table[] = {&one, &two};
+
+int
+main()
+{
+  int i, x;
+  fptr p = &one;
+
+  one (3);
+
+  for (i = 0; i < 350000000; i++)
+    {
+      x = (*p) (3);
+      p = table[x];
+    }
+  printf ("done:%d\n", x);
+}
+
+/* { dg-final-use-not-autofdo { scan-ipa-dump "2 \\(200.00%\\) speculations produced." "profile_estimate" } } */
diff --git a/gcc/testsuite/lib/scandump.exp b/gcc/testsuite/lib/scandump.exp
index 42f5c01aa60..8b7cd7cfc16 100644
--- a/gcc/testsuite/lib/scandump.exp
+++ b/gcc/testsuite/lib/scandump.exp
@@ -70,6 +70,7 @@ proc scan-dump { args } {
     set output_file "[glob -nocomplain $dumpbase.[lindex $args 2]]"
     if { $output_file == "" } {
 	verbose -log "$testcase: dump file does not exist"
+	verbose -log "dump file: $dumpbase.$suf"
 	unresolved "$testname"
 	return
     }
diff --git a/gcc/testsuite/lib/scanwpaipa.exp b/gcc/testsuite/lib/scanwpaipa.exp
index b5549fd688e..8aafd6c82e8 100644
--- a/gcc/testsuite/lib/scanwpaipa.exp
+++ b/gcc/testsuite/lib/scanwpaipa.exp
@@ -45,6 +45,29 @@ proc scan-wpa-ipa-dump { args } {
     }
 }
 
+# Argument 0 is the regexp to match
+# Argument 1 is the name of the dumped ipa pass
+# Argument 2 handles expected failures and the like
+proc scan-pgo-wpa-ipa-dump { args } {
+
+    if { [llength $args] < 2 } {
+	error "scan-pgo-wpa-ipa-dump: too few arguments"
+	return
+    }
+    if { [llength $args] > 3 } {
+	error "scan-pgo-wpa-ipa-dump: too many arguments"
+	return
+    }
+    if { [llength $args] >= 3 } {
+	scan-dump "pgo-wpa-ipa" [lindex $args 0] \
+		  "\[0-9\]\[0-9\]\[0-9\]i.[lindex $args 1]" ".x02.wpa" \
+		  [lindex $args 2]
+    } else {
+	scan-dump "pgo-wpa-ipa" [lindex $args 0] \
+		  "\[0-9\]\[0-9\]\[0-9\]i.[lindex $args 1]" ".x02.wpa"
+    }
+}
+
 # Call pass if pattern is present given number of times, otherwise fail.
 # Argument 0 is the regexp to match
 # Argument 1 is number of times the regexp must be found
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index 720f50eefec..8680abd3bed 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -2187,9 +2187,10 @@ copy_bb (copy_body_data *id, basic_block bb,
 					  num, den,
 					  true);
 
-		      /* Speculative calls consist of two edges - direct and
-			 indirect.  Duplicate the whole thing and distribute
-			 frequencies accordingly.  */
+		      /* A speculative call is consist of edges - indirect edge
+			 and direct edges (one indirect edeg may has multiple
+			 direct edges).  Duplicate the whole thing and
+			 distribute frequencies accordingly.  */
 		      if (edge->speculative)
 			{
 			  struct cgraph_edge *direct, *indirect;
@@ -2197,8 +2198,33 @@ copy_bb (copy_body_data *id, basic_block bb,
 
 			  gcc_assert (!edge->indirect_unknown_callee);
 			  old_edge->speculative_call_info (direct, indirect, ref);
+			  while (old_edge->next_callee
+				 && old_edge->next_callee->speculative
+				 && indirect->num_speculative_call_targets_p ()
+				      > 1)
+			    {
+			      id->dst_node->clone_reference (ref, stmt);
+
+			      edge = old_edge->next_callee;
+			      edge = edge->clone (id->dst_node, call_stmt,
+						  gimple_uid (stmt), num, den,
+						  true);
+			      old_edge = old_edge->next_callee;
+			      gcc_assert (!edge->indirect_unknown_callee);
+
+			      /* If the indirect edge has multiple speculative
+				 calls, iterate through all direct calls
+				 associated to the speculative call and clone
+				 all related direct edges before cloning the
+				 related indirect edge.  */
+			      old_edge->speculative_call_info (direct, indirect,
+							       ref);
+			    }
 
 			  profile_count indir_cnt = indirect->count;
+
+			  /* Duplicate the indirect edge after all direct edges
+			     cloned.  */
 			  indirect = indirect->clone (id->dst_node, call_stmt,
 						      gimple_uid (stmt),
 						      num, den,
diff --git a/gcc/value-prof.c b/gcc/value-prof.c
index cc3542f0295..f64f515c1ee 100644
--- a/gcc/value-prof.c
+++ b/gcc/value-prof.c
@@ -106,7 +106,7 @@ static bool gimple_divmod_fixed_value_transform (gimple_stmt_iterator *);
 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 gimple_ic_transform (gimple_stmt_iterator *);
 
 /* Allocate histogram value.  */
 
@@ -616,8 +616,7 @@ gimple_value_profile_transformations (void)
 	  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;
@@ -628,6 +627,9 @@ gimple_value_profile_transformations (void)
 		  gsi = gsi_for_stmt (stmt);
 		}
 	    }
+
+	  /* The function never thansforms a GIMPLE statement.  */
+	  gimple_ic_transform (&gsi);
         }
     }
 
@@ -1386,13 +1388,12 @@ gimple_ic (gcall *icall_stmt, struct cgraph_node *direct_call,
   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:
- */
+/* There maybe multiple indirect targets in histogram.  Check every
+   indirect/virtual call if callee function exists, if not exist, leave it to
+   LTO stage for later process.  Modify code of this indirect call to an if-else
+   structure in ipa-profile finally.  */
 
-static bool
+static void
 gimple_ic_transform (gimple_stmt_iterator *gsi)
 {
   gcall *stmt;
@@ -1402,52 +1403,58 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
 
   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;
+    return;
 
-  if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
-				  &count, &all))
-    return false;
+  count = 0;
+  all = histogram->hvalue.counters[0];
 
-  if (4 * count <= 3 * all)
-    return false;
+  for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
+    {
+      if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
+				      &count, &all, j))
+	return;
 
-  direct_call = find_func_by_profile_id ((int)val);
+      /* Minimum probability.  should be higher than 25%.  */
+      if (4 * count <= all)
+	return;
 
-  if (direct_call == NULL)
-    {
-      if (val)
+      direct_call = find_func_by_profile_id ((int) val);
+
+      if (direct_call == NULL)
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
-			     "Indirect call -> direct call from other "
-			     "module %T=> %i (will resolve only with LTO)\n",
-			     gimple_call_fn (stmt), (int)val);
+	  if (val)
+	    {
+	      if (dump_enabled_p ())
+		dump_printf_loc (
+		  MSG_MISSED_OPTIMIZATION, stmt,
+		  "Indirect call -> direct call from other "
+		  "module %T=> %i (will resolve only with LTO)\n",
+		  gimple_call_fn (stmt), (int) val);
+	    }
+	  return;
 	}
-      return false;
-    }
 
-  if (dump_enabled_p ())
-    {
-      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
-		       "Indirect call -> direct call "
-		       "%T => %T transformation on insn postponed\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_enabled_p ())
+	{
+	  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
+			   "Indirect call -> direct call "
+			   "%T => %T transformation on insn postponed\n",
+			   gimple_call_fn (stmt), direct_call->decl);
+	  dump_printf_loc (MSG_NOTE, stmt,
+			   "hist->count %" PRId64 " hist->all %" PRId64 "\n",
+			   count, all);
+	}
     }
-
-  return true;
 }
 
 /* Return true if the stringop CALL shall be profiled.  SIZE_ARG be
diff --git a/gcc/value-prof.h b/gcc/value-prof.h
index 77c06f60096..b3eeb57d37d 100644
--- a/gcc/value-prof.h
+++ b/gcc/value-prof.h
@@ -89,7 +89,6 @@ void verify_histograms (void);
 void free_histograms (function *);
 void stringop_block_profile (gimple *, unsigned int *, HOST_WIDE_INT *);
 gcall *gimple_ic (gcall *, struct cgraph_node *, profile_probability);
-bool check_ic_target (gcall *, struct cgraph_node *);
 bool get_nth_most_common_value (gimple *stmt, const char *counter_type,
 				histogram_value hist, gcov_type *value,
 				gcov_type *count, gcov_type *all,
-- 
2.21.0.777.g83232e3864

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v7] Missed function specialization + partial devirtualization
  2019-12-26 13:42                                   ` [PATCH v7] " luoxhu
@ 2020-01-10 11:23                                     ` Jan Hubicka
  2020-01-13  8:47                                       ` luoxhu
  0 siblings, 1 reply; 38+ messages in thread
From: Jan Hubicka @ 2020-01-10 11:23 UTC (permalink / raw)
  To: luoxhu; +Cc: Martin Liška, gcc-patches, segher, wschmidt

> > I think you are also not removing the common_target and
> > common_target_probability from cgraph_edge.
> > There is now code in ipa-utils merging the histograms. You will need to
> > update that to your representation. It should not be hard - it either
> > copies all the values from target function or merges them with given
> > probability scales.
> 
> This seems a bit tricky for me.  ipa-icf runs after ipa-profile, the reason why 
> remove <common_target_id, common_target_probability> is they are never used in
> later passes during previous discussion.

There is also merging done from lto-symtab.  If you have COMDAT inline
which is late inlined at -fprofile-generate time you will end up with
multiple profiles for it and they needs to be merged.  This is done
before IPA passes including ipa-profile.
> 
> I added a new variable target_prob to save it in direct edge when calling
> make_speculative, since the target_prob is assigned in ipa-profile, so no
> need to stream out and in through LTO like speculative_id.
> 
> Even though, scaling in ipa-icf with multiple direct edges is more complicate then
> single direct edge, need double circulation to find each src to dst edge map and do
> scaling one by one. 
> 
> BTW, the breaking patch r279373 "Fix merging of common traget info." only mentioned
> firefox but doesn't include a testcase, I tried to construct some test
> cases, but fail to cover all the circumstance (I just implemented the N:N map yet, 
> please refer to the code in below patch.), do you have cases other than firefox to
> cover it?  Or how to test it as r279373 required?  As this piece of code is not quite
> related to this patch, can we just leave it for future refine?  Thanks :)

You need testcase with a comdat that is used & inlined in two units,
contains indirect call and build with -fno-early-inlining.

However we can handle this incrementally.

> 	2019-12-26  Xiong Hu Luo  <luoxhu@linux.ibm.com>
> 
> 	PR ipa/69678
> 	* cgraph.c (symbol_table::create_edge): Init speculative_id and
> 	target_prob.
> 	(cgraph_edge::make_speculative): Add param for setting speculative_id
> 	and target_prob.
> 	(cgraph_edge::speculative_call_info): Update comments and find reference
> 	by speculative_id for multiple indirect targets.
> 	(cgraph_edge::resolve_speculation): Decrease the speculations
> 	for indirect edge, drop it's speculative if not direct target
> 	left. Update comments.
> 	(cgraph_edge::redirect_call_stmt_to_callee): Likewise.
> 	(cgraph_node::dump): Print num_speculative_call_targets.
> 	(cgraph_node::verify_node): Don't report error if speculative
> 	edge not include statement.
> 	(cgraph_edge::num_speculative_call_targets_p): New function.
> 	* cgraph.h (int common_target_id): Remove.
> 	(int common_target_probability): Remove.
> 	(num_speculative_call_targets): New variable.
> 	(make_speculative): Add param for setting speculative_id.
> 	(cgraph_edge::num_speculative_call_targets_p): New declare.
> 	(target_prob): New variable.
> 	(speculative_id): New variable.
> 	* ipa-fnsummary.c (analyze_function_body): Create and duplicate
> 	  call summaries for multiple speculative call targets.
> 	* cgraphclones.c (cgraph_node::create_clone): Clone speculative_id.
> 	* ipa-profile.c (struct speculative_call_target): New struct.
> 	(class speculative_call_summary): New class.
> 	(class speculative_call_summaries): New class.
> 	(call_sums): New variable.
> 	(ipa_profile_generate_summary): Generate indirect multiple targets summaries.
> 	(ipa_profile_write_edge_summary): New function.
> 	(ipa_profile_write_summary): Stream out indirect multiple targets summaries.
> 	(ipa_profile_dump_all_summaries): New function.
> 	(ipa_profile_read_edge_summary): New function.
> 	(ipa_profile_read_summary_section): New function.
> 	(ipa_profile_read_summary): Stream in indirect multiple targets summaries.
> 	(ipa_profile): Generate num_speculative_call_targets from
> 	profile summaries.
> 	* ipa-ref.h (speculative_id): New variable.
> 	* ipa-utils.c (ipa_merge_profiles): Update with target_prob.
> 	* lto-cgraph.c (lto_output_edge): Remove indirect common_target_id and
> 	common_target_probability.   Stream out speculative_id and
> 	num_speculative_call_targets.
> 	(input_edge): Likewise.
> 	* predict.c (dump_prediction): Remove edges count assert to be
> 	precise.
> 	* symtab.c (symtab_node::create_reference): Init speculative_id.
> 	(symtab_node::clone_references): Clone speculative_id.
> 	(symtab_node::clone_referring): Clone speculative_id.
> 	(symtab_node::clone_reference): Clone speculative_id.
> 	(symtab_node::clear_stmts_in_references): Clear speculative_id.
> 	* tree-inline.c (copy_bb): Duplicate all the speculative edges
> 	if indirect call contains multiple speculative targets.
> 	* value-prof.h  (check_ic_target): Remove.
> 	* value-prof.c  (gimple_value_profile_transformations):
> 	Use void function gimple_ic_transform.
> 	* value-prof.c  (gimple_ic_transform): Handle topn case.
> 	Fix comment typos.  Change it to a void function.
> 
> gcc/testsuite/ChangeLog
> 
> 	2019-12-26  Xiong Hu Luo  <luoxhu@linux.ibm.com>
> 
> 	PR ipa/69678
> 	* gcc.dg/tree-prof/indir-call-prof-topn.c: New testcase.
> 	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c: New testcase.
> 	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c: New testcase.
> 	* gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c: New testcase.
> 	* lib/scandump.exp: Dump executable file name.
> 	* lib/scanwpaipa.exp: New scan-pgo-wap-ipa-dump.

OK. You will need to do the obvious updates for Martin's patch
which turned some member functions into static functions.

Honza

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v7] Missed function specialization + partial devirtualization
  2020-01-10 11:23                                     ` Jan Hubicka
@ 2020-01-13  8:47                                       ` luoxhu
  2020-01-14 13:37                                         ` Martin Liška
  2020-01-14 20:14                                         ` David Malcolm
  0 siblings, 2 replies; 38+ messages in thread
From: luoxhu @ 2020-01-13  8:47 UTC (permalink / raw)
  To: Jan Hubicka; +Cc: Martin Liška, gcc-patches, segher, wschmidt

On 2020/1/10 19:08, Jan Hubicka wrote:
> OK. You will need to do the obvious updates for Martin's patch
> which turned some member functions into static functions.
> 
> Honza

Thanks a lot!  Rebased & updated, will commit below patch shortly when git push is ready.


v8:
 1. Rebase to master with Martin's static function (r280043) comments merge.
    Boostrap/testsuite/SPEC2017 tested pass on Power8-LE.
 2. TODO:
    2.1. C++ devirt for multiple speculative call targets.
    2.2. ipa-icf ipa_merge_profiles refine with COMDAT inline testcase.


This patch aims to fix PR69678 caused by PGO indirect call profiling
performance issues.
The bug that profiling data is never working was fixed by Martin's pull
back of topN patches, performance got GEOMEAN ~1% improvement(+24% for
511.povray_r specifically).
Still, currently the default profile only generates SINGLE indirect target
that called more than 75%.  This patch leverages MULTIPLE indirect
targets use in LTO-WPA and LTO-LTRANS stage, as a result, function
specialization, profiling, partial devirtualization, inlining and
cloning could be done successfully based on it.
Performance can get improved from 0.70 sec to 0.38 sec on simple tests.
Details are:
  1.  PGO with topn is enabled by default now, but only one indirect
  target edge will be generated in ipa-profile pass, so add variables to enable
  multiple speculative edges through passes, speculative_id will record the
  direct edge index bind to the indirect edge, indirect_call_targets length
  records how many direct edges owned by the indirect edge, postpone gimple_ic
  to ipa-profile like default as inline pass will decide whether it is benefit
  to transform indirect call.
  2.  Use speculative_id to track and search the reference node matched
  with the direct edge's callee for multiple targets.  Actually, it is the
  caller's responsibility to handle the direct edges mapped to same indirect
  edge.  speculative_call_info will return one of the direct edge specified,
  this will leverage current IPA edge process framework mostly.
  3.  Enable LTO WPA/LTRANS stage multiple indirect call targets analysis for
  profile full support in ipa passes and cgraph_edge functions.  speculative_id
  can be set by make_speculative id when multiple targets are binded to
  one indirect edge, and cloned if new edge is cloned.  speculative_id
  is streamed out and stream int by lto like lto_stmt_uid.
  4.  Create and duplicate all speculative direct edge's call summary
  in ipa-fnsummary.c with auto_vec.
  5.  Add 1 in module testcase and 2 cross module testcases.
  6.  Bootstrap and regression test passed on Power8-LE.  No function
  and performance regression for SPEC2017.

gcc/ChangeLog

	2020-01-13  Xiong Hu Luo  <luoxhu@linux.ibm.com>

	PR ipa/69678
	* cgraph.c (symbol_table::create_edge): Init speculative_id and
	target_prob.
	(cgraph_edge::make_speculative): Add param for setting speculative_id
	and target_prob.
	(cgraph_edge::speculative_call_info): Update comments and find reference
	by speculative_id for multiple indirect targets.
	(cgraph_edge::resolve_speculation): Decrease the speculations
	for indirect edge, drop it's speculative if not direct target
	left. Update comments.
	(cgraph_edge::redirect_call_stmt_to_callee): Likewise.
	(cgraph_node::dump): Print num_speculative_call_targets.
	(cgraph_node::verify_node): Don't report error if speculative
	edge not include statement.
	(cgraph_edge::num_speculative_call_targets_p): New function.
	* cgraph.h (int common_target_id): Remove.
	(int common_target_probability): Remove.
	(num_speculative_call_targets): New variable.
	(make_speculative): Add param for setting speculative_id.
	(cgraph_edge::num_speculative_call_targets_p): New declare.
	(target_prob): New variable.
	(speculative_id): New variable.
	* ipa-fnsummary.c (analyze_function_body): Create and duplicate
	  call summaries for multiple speculative call targets.
	* cgraphclones.c (cgraph_node::create_clone): Clone speculative_id.
	* ipa-profile.c (struct speculative_call_target): New struct.
	(class speculative_call_summary): New class.
	(class speculative_call_summaries): New class.
	(call_sums): New variable.
	(ipa_profile_generate_summary): Generate indirect multiple targets summaries.
	(ipa_profile_write_edge_summary): New function.
	(ipa_profile_write_summary): Stream out indirect multiple targets summaries.
	(ipa_profile_dump_all_summaries): New function.
	(ipa_profile_read_edge_summary): New function.
	(ipa_profile_read_summary_section): New function.
	(ipa_profile_read_summary): Stream in indirect multiple targets summaries.
	(ipa_profile): Generate num_speculative_call_targets from
	profile summaries.
	* ipa-ref.h (speculative_id): New variable.
	* ipa-utils.c (ipa_merge_profiles): Update with target_prob.
	* lto-cgraph.c (lto_output_edge): Remove indirect common_target_id and
	common_target_probability.   Stream out speculative_id and
	num_speculative_call_targets.
	(input_edge): Likewise.
	* predict.c (dump_prediction): Remove edges count assert to be
	precise.
	* symtab.c (symtab_node::create_reference): Init speculative_id.
	(symtab_node::clone_references): Clone speculative_id.
	(symtab_node::clone_referring): Clone speculative_id.
	(symtab_node::clone_reference): Clone speculative_id.
	(symtab_node::clear_stmts_in_references): Clear speculative_id.
	* tree-inline.c (copy_bb): Duplicate all the speculative edges
	if indirect call contains multiple speculative targets.
	* value-prof.h  (check_ic_target): Remove.
	* value-prof.c  (gimple_value_profile_transformations):
	Use void function gimple_ic_transform.
	* value-prof.c  (gimple_ic_transform): Handle topn case.
	Fix comment typos.  Change it to a void function.

gcc/testsuite/ChangeLog

	2020-01-13  Xiong Hu Luo  <luoxhu@linux.ibm.com>

	PR ipa/69678
	* gcc.dg/tree-prof/indir-call-prof-topn.c: New testcase.
	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c: New testcase.
	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c: New testcase.
	* gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c: New testcase.
	* lib/scandump.exp: Dump executable file name.
	* lib/scanwpaipa.exp: New scan-pgo-wap-ipa-dump.
---
 gcc/ChangeLog                                 |  61 +++
 gcc/cgraph.c                                  | 117 +++++-
 gcc/cgraph.h                                  |  51 ++-
 gcc/cgraphclones.c                            |   1 +
 gcc/ipa-fnsummary.c                           |  22 +-
 gcc/ipa-profile.c                             | 353 ++++++++++++++++--
 gcc/ipa-ref.h                                 |   3 +
 gcc/ipa-utils.c                               | 173 ++++++---
 gcc/lto-cgraph.c                              |  30 +-
 gcc/predict.c                                 |   1 -
 gcc/symtab.c                                  |   5 +
 gcc/testsuite/ChangeLog                       |  10 +
 .../tree-prof/crossmodule-indir-call-topn-1.c |  33 ++
 .../crossmodule-indir-call-topn-1a.c          |  22 ++
 .../tree-prof/crossmodule-indir-call-topn-2.c |  40 ++
 .../gcc.dg/tree-prof/indir-call-prof-topn.c   |  37 ++
 gcc/testsuite/lib/scandump.exp                |   1 +
 gcc/testsuite/lib/scanwpaipa.exp              |  23 ++
 gcc/tree-inline.c                             |  32 +-
 gcc/value-prof.c                              |  87 +++--
 gcc/value-prof.h                              |   1 -
 21 files changed, 934 insertions(+), 169 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index a195863212e..a1a7576ba9d 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,64 @@
+2020-01-13  Xiong Hu Luo  <luoxhu@linux.ibm.com>
+
+	PR ipa/69678
+	* cgraph.c (symbol_table::create_edge): Init speculative_id and
+	target_prob.
+	(cgraph_edge::make_speculative): Add param for setting speculative_id
+	and target_prob.
+	(cgraph_edge::speculative_call_info): Update comments and find reference
+	by speculative_id for multiple indirect targets.
+	(cgraph_edge::resolve_speculation): Decrease the speculations
+	for indirect edge, drop it's speculative if not direct target
+	left. Update comments.
+	(cgraph_edge::redirect_call_stmt_to_callee): Likewise.
+	(cgraph_node::dump): Print num_speculative_call_targets.
+	(cgraph_node::verify_node): Don't report error if speculative
+	edge not include statement.
+	(cgraph_edge::num_speculative_call_targets_p): New function.
+	* cgraph.h (int common_target_id): Remove.
+	(int common_target_probability): Remove.
+	(num_speculative_call_targets): New variable.
+	(make_speculative): Add param for setting speculative_id.
+	(cgraph_edge::num_speculative_call_targets_p): New declare.
+	(target_prob): New variable.
+	(speculative_id): New variable.
+	* ipa-fnsummary.c (analyze_function_body): Create and duplicate
+	  call summaries for multiple speculative call targets.
+	* cgraphclones.c (cgraph_node::create_clone): Clone speculative_id.
+	* ipa-profile.c (struct speculative_call_target): New struct.
+	(class speculative_call_summary): New class.
+	(class speculative_call_summaries): New class.
+	(call_sums): New variable.
+	(ipa_profile_generate_summary): Generate indirect multiple targets summaries.
+	(ipa_profile_write_edge_summary): New function.
+	(ipa_profile_write_summary): Stream out indirect multiple targets summaries.
+	(ipa_profile_dump_all_summaries): New function.
+	(ipa_profile_read_edge_summary): New function.
+	(ipa_profile_read_summary_section): New function.
+	(ipa_profile_read_summary): Stream in indirect multiple targets summaries.
+	(ipa_profile): Generate num_speculative_call_targets from
+	profile summaries.
+	* ipa-ref.h (speculative_id): New variable.
+	* ipa-utils.c (ipa_merge_profiles): Update with target_prob.
+	* lto-cgraph.c (lto_output_edge): Remove indirect common_target_id and
+	common_target_probability.   Stream out speculative_id and
+	num_speculative_call_targets.
+	(input_edge): Likewise.
+	* predict.c (dump_prediction): Remove edges count assert to be
+	precise.
+	* symtab.c (symtab_node::create_reference): Init speculative_id.
+	(symtab_node::clone_references): Clone speculative_id.
+	(symtab_node::clone_referring): Clone speculative_id.
+	(symtab_node::clone_reference): Clone speculative_id.
+	(symtab_node::clear_stmts_in_references): Clear speculative_id.
+	* tree-inline.c (copy_bb): Duplicate all the speculative edges
+	if indirect call contains multiple speculative targets.
+	* value-prof.h  (check_ic_target): Remove.
+	* value-prof.c  (gimple_value_profile_transformations):
+	Use void function gimple_ic_transform.
+	* value-prof.c  (gimple_ic_transform): Handle topn case.
+	Fix comment typos.  Change it to a void function.
+
 2020-01-10  Thomas Schwinge  <thomas@codesourcery.com>
 
 	* tree.h (OMP_CLAUSE_USE_DEVICE_PTR_IF_PRESENT): New definition.
diff --git a/gcc/cgraph.c b/gcc/cgraph.c
index fe3f06726d4..95b523d6be5 100644
--- a/gcc/cgraph.c
+++ b/gcc/cgraph.c
@@ -858,6 +858,8 @@ symbol_table::create_edge (cgraph_node *caller, cgraph_node *callee,
   edge->prev_callee = NULL;
   edge->next_callee = NULL;
   edge->lto_stmt_uid = 0;
+  edge->target_prob = 0;
+  edge->speculative_id = 0;
 
   edge->count = count;
   edge->call_stmt = call_stmt;
@@ -1044,10 +1046,16 @@ cgraph_edge::remove (cgraph_edge *edge)
    the reference representing the if conditional and attaches
    them all to the original indirect call statement.  
 
+   speculative_id is used to link direct calls with their corresponding
+   IPA_REF_ADDR references when representing speculative calls.
+
+   target_prob is the probability of the speculative call.
+
    Return direct edge created.  */
 
 cgraph_edge *
-cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
+cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count,
+			       unsigned int speculative_id, int target_prob)
 {
   cgraph_node *n = caller;
   ipa_ref *ref = NULL;
@@ -1065,24 +1073,53 @@ cgraph_edge::make_speculative (cgraph_node *n2, profile_count direct_count)
   else
     e2->can_throw_external = can_throw_external;
   e2->lto_stmt_uid = lto_stmt_uid;
+  e2->speculative_id = speculative_id;
+  e2->target_prob = target_prob;
   e2->in_polymorphic_cdtor = in_polymorphic_cdtor;
   count -= e2->count;
   symtab->call_edge_duplication_hooks (this, e2);
   ref = n->create_reference (n2, IPA_REF_ADDR, call_stmt);
   ref->lto_stmt_uid = lto_stmt_uid;
+  ref->speculative_id = speculative_id;
   ref->speculative = speculative;
   n2->mark_address_taken ();
   return e2;
 }
 
-/* Speculative call consist of three components:
-   1) an indirect edge representing the original call
-   2) an direct edge representing the new call
-   3) ADDR_EXPR reference representing the speculative check.
-   All three components are attached to single statement (the indirect
-   call) and if one of them exists, all of them must exist.
+/* Speculative calls represent a transformation of indirect calls
+   which may be later inserted into gimple in the following form:
+
+   if (call_dest == target1)
+   target1 ();
+   else if (call_dest == target2)
+   target2 ();
+   else
+   call_dest ();
+
+   This is a win in the case when target1 and target2 are common values for
+   call_dest as determined by ipa-devirt or indirect call profiling.
+   In particular this may enable inlining and other optimizations.
+
+   Speculative call consists of the following main components:
+
+   1) One or more "speculative" direct call (num_speculative_call_targets is
+   speculative direct call count belongs to the speculative indirect call)
+   2) One or more IPA_REF_ADDR references (representing the fact that code above
+   takes address of target1 and target2)
+   3) The fallback "speculative" indirect call
 
-   Given speculative call edge, return all three components.
+   Direct calls and corresponding references are linked by
+   speculative_id.
+
+   speculative_call_info returns triple
+   (direct_call, indirect call, IPA_REF_ADDR reference)
+   when called on one edge participating in the speculative call:
+
+   1) If called on direct call, its corresponding IPA_REF_ADDR and related
+   indirect call are returned.
+
+   2) If called on indirect call, it will return one of direct edges and its
+   matching IPA_REF_ADDR.
  */
 
 void
@@ -1122,7 +1159,7 @@ cgraph_edge::speculative_call_info (cgraph_edge *&direct,
 
   reference = NULL;
   for (i = 0; e->caller->iterate_reference (i, ref); i++)
-    if (ref->speculative
+    if (ref->speculative && ref->speculative_id == e->speculative_id
 	&& ((ref->stmt && ref->stmt == e->call_stmt)
 	    || (!ref->stmt && ref->lto_stmt_uid == e->lto_stmt_uid)))
       {
@@ -1138,9 +1175,18 @@ cgraph_edge::speculative_call_info (cgraph_edge *&direct,
 
 /* Speculative call EDGE turned out to be direct call to CALLEE_DECL.  Remove
    the speculative call sequence and return edge representing the call, the
-   original EDGE can be removed and deallocated.  It is up to caller to
-   redirect the call as appropriate.  Return the edge that now represents the
-   call.  */
+   original EDGE can be removed and deallocated.  Return the edge that now
+   represents the call.
+
+   For "speculative" indirect call that contains multiple "speculative"
+   targets (i.e. edge->indirect_info->num_speculative_call_targets > 1),
+   decrease the count and only remove current direct edge.
+
+   If no speculative direct call left to the speculative indirect call, remove
+   the speculative of both the indirect call and corresponding direct edge.
+
+   It is up to caller to iteratively resolve each "speculative" direct call and
+   redirect the call as appropriate.  */
 
 cgraph_edge *
 cgraph_edge::resolve_speculation (cgraph_edge *edge, tree callee_decl)
@@ -1184,7 +1230,16 @@ cgraph_edge::resolve_speculation (cgraph_edge *edge, tree callee_decl)
          in the functions inlined through it.  */
     }
   edge->count += e2->count;
-  edge->speculative = false;
+  if (edge->num_speculative_call_targets_p ())
+    {
+      /* The indirect edge has multiple speculative targets, don't remove
+	 speculative until all related direct edges are resolved.  */
+      edge->indirect_info->num_speculative_call_targets--;
+      if (!edge->indirect_info->num_speculative_call_targets)
+	edge->speculative = false;
+    }
+  else
+    edge->speculative = false;
   e2->speculative = false;
   ref->remove_reference ();
   if (e2->indirect_unknown_callee || e2->inline_failed)
@@ -1244,7 +1299,17 @@ cgraph_edge::make_direct (cgraph_edge *edge, cgraph_node *callee)
 
 /* If necessary, change the function declaration in the call statement
    associated with E so that it corresponds to the edge callee.  Speculations
-   can be resolved in the process and EDGE can be removed and deallocated.  */
+   can be resolved in the process and EDGE can be removed and deallocated.
+
+   The edge could be one of speculative direct call generated from speculative
+   indirect call.  In this circumstance, decrease the speculative targets
+   count (i.e. num_speculative_call_targets) and redirect call stmt to the
+   corresponding i-th target.  If no speculative direct call left to the
+   speculative indirect call, remove "speculative" of the indirect call and
+   also redirect stmt to it's final direct target.
+
+   It is up to caller to iteratively transform each "speculative"
+   direct call as appropriate.  */
 
 gimple *
 cgraph_edge::redirect_call_stmt_to_callee (cgraph_edge *e)
@@ -1290,7 +1355,17 @@ cgraph_edge::redirect_call_stmt_to_callee (cgraph_edge *e)
 	  e->caller->set_call_stmt_including_clones (e->call_stmt, new_stmt,
 						     false);
 	  e->count = gimple_bb (e->call_stmt)->count;
-	  e2->speculative = false;
+	  if (e2->num_speculative_call_targets_p ())
+	    {
+	      /* The indirect edge has multiple speculative targets, don't
+		 remove speculative until all related direct edges are
+		 redirected.  */
+	      e2->indirect_info->num_speculative_call_targets--;
+	      if (!e2->indirect_info->num_speculative_call_targets)
+		e2->speculative = false;
+	    }
+	  else
+	    e2->speculative = false;
 	  e2->count = gimple_bb (e2->call_stmt)->count;
 	  ref->speculative = false;
 	  ref->stmt = NULL;
@@ -2103,6 +2178,8 @@ cgraph_node::dump (FILE *f)
 	  if (edge->indirect_info->vptr_changed)
 	    fprintf (f, " (vptr maybe changed)");
 	}
+      fprintf (f, " Num speculative call targets: %i",
+	       edge->indirect_info->num_speculative_call_targets);
       fprintf (f, "\n");
       if (edge->indirect_info->polymorphic)
 	edge->indirect_info->context.dump (f);
@@ -3393,7 +3470,7 @@ cgraph_node::verify_node (void)
 
       for (e = callees; e; e = e->next_callee)
 	{
-	  if (!e->aux)
+	  if (!e->aux && !e->speculative)
 	    {
 	      error ("edge %s->%s has no corresponding call_stmt",
 		     identifier_to_locale (e->caller->name ()),
@@ -3732,6 +3809,14 @@ cgraph_edge::possibly_call_in_translation_unit_p (void)
   return node->get_availability () >= AVAIL_INTERPOSABLE;
 }
 
+/* Return num_speculative_targets of this edge.  */
+
+int
+cgraph_edge::num_speculative_call_targets_p (void)
+{
+  return indirect_info ? indirect_info->num_speculative_call_targets : 0;
+}
+
 /* A stashed copy of "symtab" for use by selftest::symbol_table_test.
    This needs to be a global so that it can be a GC root, and thus
    prevent the stashed copy from being garbage-collected if the GC runs
diff --git a/gcc/cgraph.h b/gcc/cgraph.h
index 71cd902312e..0ace13df1f9 100644
--- a/gcc/cgraph.h
+++ b/gcc/cgraph.h
@@ -1703,10 +1703,9 @@ public:
   int param_index;
   /* ECF flags determined from the caller.  */
   int ecf_flags;
-  /* Profile_id of common target obtained from profile.  */
-  int common_target_id;
-  /* Probability that call will land in function with COMMON_TARGET_ID.  */
-  int common_target_probability;
+
+  /* Number of speculative call targets, it's less than GCOV_TOPN_VALUES.  */
+  unsigned num_speculative_call_targets : 16;
 
   /* Set when the call is a virtual call with the parameter being the
      associated object pointer rather than a simple direct call.  */
@@ -1764,10 +1763,14 @@ public:
 
   /* Turn edge into speculative call calling N2. Update
      the profile so the direct call is taken COUNT times
-     with FREQUENCY.  */
-  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count);
-
-   /* Given speculative call edge, return all three components.  */
+     with FREQUENCY.  speculative_id is used to link direct calls with their
+     corresponding IPA_REF_ADDR references when representing speculative calls.
+     target_prob is the probability of the speculative call.  */
+  cgraph_edge *make_speculative (cgraph_node *n2, profile_count direct_count,
+				 unsigned int speculative_id = 0,
+				 int target_prob = 0);
+
+  /* Given speculative call edge, return all three components.  */
   void speculative_call_info (cgraph_edge *&direct, cgraph_edge *&indirect,
 			      ipa_ref *&reference);
 
@@ -1775,14 +1778,34 @@ public:
      the speculative call sequence and return edge representing the call, the
      original EDGE can be removed and deallocated.  It is up to caller to
      redirect the call as appropriate.  Return the edge that now represents the
-     call.  */
+     call.
+
+     For "speculative" indirect call that contains multiple "speculative"
+     targets (i.e. edge->indirect_info->num_speculative_call_targets > 1),
+     decrease the count and only remove current direct edge.
+
+     If no speculative direct call left to the speculative indirect call, remove
+     the speculative of both the indirect call and corresponding direct edge.
+
+     It is up to caller to iteratively resolve each "speculative" direct call
+     and redirect the call as appropriate.  */
   static cgraph_edge *resolve_speculation (cgraph_edge *edge,
 					   tree callee_decl = NULL);
 
   /* If necessary, change the function declaration in the call statement
      associated with edge E so that it corresponds to the edge callee.
      Speculations can be resolved in the process and EDGE can be removed and
-     deallocated.  */
+     deallocated.
+
+     The edge could be one of speculative direct call generated from speculative
+     indirect call.  In this circumstance, decrease the speculative targets
+     count (i.e. num_speculative_call_targets) and redirect call stmt to the
+     corresponding i-th target.  If no speculative direct call left to the
+     speculative indirect call, remove "speculative" of the indirect call and
+     also redirect stmt to it's final direct target.
+
+     It is up to caller to iteratively transform each "speculative"
+     direct call as appropriate.  */
   static gimple *redirect_call_stmt_to_callee (cgraph_edge *e);
 
   /* Create clone of edge in the node N represented
@@ -1829,6 +1852,9 @@ public:
      be internal to the current translation unit.  */
   bool possibly_call_in_translation_unit_p (void);
 
+  /* Return num_speculative_targets of this edge.  */
+  int num_speculative_call_targets_p (void);
+
   /* Expected number of executions: calculated in profile.c.  */
   profile_count count;
   cgraph_node *caller;
@@ -1848,6 +1874,11 @@ public:
   /* The stmt_uid of call_stmt.  This is used by LTO to recover the call_stmt
      when the function is serialized in.  */
   unsigned int lto_stmt_uid;
+  /*  target_prob is the probability of the speculative call.  */
+  unsigned int target_prob;
+  /* speculative id is used to link direct calls with their corresponding
+     IPA_REF_ADDR references when representing speculative calls.  */
+  unsigned int speculative_id : 16;
   /* Whether this edge was made direct by indirect inlining.  */
   unsigned int indirect_inlining_edge : 1;
   /* Whether this edge describes an indirect call with an undetermined
diff --git a/gcc/cgraphclones.c b/gcc/cgraphclones.c
index c186fb98cf5..e73e0696b91 100644
--- a/gcc/cgraphclones.c
+++ b/gcc/cgraphclones.c
@@ -133,6 +133,7 @@ cgraph_edge::clone (cgraph_node *n, gcall *call_stmt, unsigned stmt_uid,
   new_edge->inline_failed = inline_failed;
   new_edge->indirect_inlining_edge = indirect_inlining_edge;
   new_edge->lto_stmt_uid = stmt_uid;
+  new_edge->speculative_id = speculative_id;
   /* Clone flags that depend on call_stmt availability manually.  */
   new_edge->can_throw_external = can_throw_external;
   new_edge->call_stmt_cannot_inline_p = call_stmt_cannot_inline_p;
diff --git a/gcc/ipa-fnsummary.c b/gcc/ipa-fnsummary.c
index efe42641b6b..4e1c587b101 100644
--- a/gcc/ipa-fnsummary.c
+++ b/gcc/ipa-fnsummary.c
@@ -2604,7 +2604,7 @@ analyze_function_body (struct cgraph_node *node, bool early)
 	      edge_set_predicate (edge, &bb_predicate);
 	      if (edge->speculative)
 		{
-		  cgraph_edge *direct, *indirect;
+		  cgraph_edge *direct, *indirect, *next_direct;
 		  ipa_ref *ref;
 		  edge->speculative_call_info (direct, indirect, ref);
 		  gcc_assert (direct == edge);
@@ -2612,6 +2612,26 @@ analyze_function_body (struct cgraph_node *node, bool early)
 			 = ipa_call_summaries->get_create (indirect);
 		  ipa_call_summaries->duplicate (edge, indirect,
 						 es, es2);
+
+		  /* Create and duplicate call summaries for multiple
+		     speculative call targets.  */
+		  int num_specs = indirect->num_speculative_call_targets_p ();
+		  if (num_specs > 1)
+		    for (next_direct = edge->next_callee;
+			 next_direct && --num_specs;
+			 next_direct = next_direct->next_callee)
+		      {
+			next_direct->speculative_call_info (direct, indirect,
+							    ref);
+			if (direct == next_direct && next_direct->speculative
+			    && edge->call_stmt == stmt)
+			  {
+			    ipa_call_summary *es3
+			      = ipa_call_summaries->get_create (next_direct);
+			    ipa_call_summaries->duplicate (edge, next_direct,
+							   es, es3);
+			  }
+		      }
 		}
 	    }
 
diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
index 017f63e80a3..fc231c916b7 100644
--- a/gcc/ipa-profile.c
+++ b/gcc/ipa-profile.c
@@ -159,7 +159,99 @@ dump_histogram (FILE *file, vec<histogram_entry *> histogram)
    }
 }
 
-/* Collect histogram from CFG profiles.  */
+/* Structure containing speculative target information from profile.  */
+
+struct speculative_call_target
+{
+  speculative_call_target (unsigned int id = 0, int prob = 0)
+    : target_id (id), target_probability (prob)
+  {
+  }
+
+  /* Profile_id of target obtained from profile.  */
+  unsigned int target_id;
+  /* Probability that call will land in function with target_id.  */
+  unsigned int target_probability;
+};
+
+class speculative_call_summary
+{
+public:
+  speculative_call_summary () : speculative_call_targets ()
+  {}
+
+  auto_vec<speculative_call_target> speculative_call_targets;
+
+  void dump (FILE *f);
+
+};
+
+  /* Class to manage call summaries.  */
+
+class ipa_profile_call_summaries
+  : public call_summary<speculative_call_summary *>
+{
+public:
+  ipa_profile_call_summaries (symbol_table *table)
+    : call_summary<speculative_call_summary *> (table)
+  {}
+
+  /* Duplicate info when an edge is cloned.  */
+  virtual void duplicate (cgraph_edge *, cgraph_edge *,
+			  speculative_call_summary *old_sum,
+			  speculative_call_summary *new_sum);
+};
+
+static ipa_profile_call_summaries *call_sums = NULL;
+
+/* Dump all information in speculative call summary to F.  */
+
+void
+speculative_call_summary::dump (FILE *f)
+{
+  cgraph_node *n2;
+
+  unsigned spec_count = speculative_call_targets.length ();
+  for (unsigned i = 0; i < spec_count; i++)
+    {
+      speculative_call_target item = speculative_call_targets[i];
+      n2 = find_func_by_profile_id (item.target_id);
+      if (n2)
+	fprintf (f, "    The %i speculative target is %s with prob %3.2f\n", i,
+		 n2->dump_name (),
+		 item.target_probability / (float) REG_BR_PROB_BASE);
+      else
+	fprintf (f, "    The %i speculative target is %u with prob %3.2f\n", i,
+		 item.target_id,
+		 item.target_probability / (float) REG_BR_PROB_BASE);
+    }
+}
+
+/* Duplicate info when an edge is cloned.  */
+
+void
+ipa_profile_call_summaries::duplicate (cgraph_edge *, cgraph_edge *,
+				       speculative_call_summary *old_sum,
+				       speculative_call_summary *new_sum)
+{
+  if (!old_sum)
+    return;
+
+  unsigned old_count = old_sum->speculative_call_targets.length ();
+  if (!old_count)
+    return;
+
+  new_sum->speculative_call_targets.reserve_exact (old_count);
+  new_sum->speculative_call_targets.quick_grow_cleared (old_count);
+
+  for (unsigned i = 0; i < old_count; i++)
+    {
+      new_sum->speculative_call_targets[i]
+	= old_sum->speculative_call_targets[i];
+    }
+}
+
+/* Collect histogram and speculative target summaries from CFG profiles.  */
 
 static void
 ipa_profile_generate_summary (void)
@@ -169,7 +261,10 @@ ipa_profile_generate_summary (void)
   basic_block bb;
 
   hash_table<histogram_hash> hashtable (10);
-  
+
+  gcc_checking_assert (!call_sums);
+  call_sums = new ipa_profile_call_summaries (symtab);
+
   FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
     if (ENTRY_BLOCK_PTR_FOR_FN (DECL_STRUCT_FUNCTION (node->decl))->count.ipa_p ())
       FOR_EACH_BB_FN (bb, DECL_STRUCT_FUNCTION (node->decl))
@@ -191,23 +286,35 @@ ipa_profile_generate_summary (void)
 		  if (h)
 		    {
 		      gcov_type val, count, all;
-		      if (get_nth_most_common_value (NULL, "indirect call", h,
-						     &val, &count, &all))
+		      struct cgraph_edge *e = node->get_edge (stmt);
+		      if (e && !e->indirect_unknown_callee)
+			continue;
+
+		      speculative_call_summary *csum
+			= call_sums->get_create (e);
+
+		      for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
 			{
-			  struct cgraph_edge * e = node->get_edge (stmt);
-			  if (e && !e->indirect_unknown_callee)
+			  if (!get_nth_most_common_value (NULL, "indirect call",
+							  h, &val, &count, &all,
+							  j))
+			    continue;
+
+			  if (val == 0)
 			    continue;
 
-			  e->indirect_info->common_target_id = val;
-			  e->indirect_info->common_target_probability
-			    = GCOV_COMPUTE_SCALE (count, all);
-			  if (e->indirect_info->common_target_probability > REG_BR_PROB_BASE)
+			  speculative_call_target item (
+			    val, GCOV_COMPUTE_SCALE (count, all));
+			  if (item.target_probability > REG_BR_PROB_BASE)
 			    {
 			      if (dump_file)
-				fprintf (dump_file, "Probability capped to 1\n");
-			      e->indirect_info->common_target_probability = REG_BR_PROB_BASE;
+				fprintf (dump_file,
+					 "Probability capped to 1\n");
+			      item.target_probability = REG_BR_PROB_BASE;
 			    }
+			  csum->speculative_call_targets.safe_push (item);
 			}
+
 		      gimple_remove_histogram_value (DECL_STRUCT_FUNCTION (node->decl),
 						      stmt, h);
 		    }
@@ -222,6 +329,33 @@ ipa_profile_generate_summary (void)
   histogram.qsort (cmp_counts);
 }
 
+/* Serialize the speculative summary info for LTO.  */
+
+static void
+ipa_profile_write_edge_summary (lto_simple_output_block *ob,
+				speculative_call_summary *csum)
+{
+  unsigned len = 0;
+
+  len = csum->speculative_call_targets.length ();
+
+  gcc_assert (len <= GCOV_TOPN_VALUES);
+
+  streamer_write_hwi_stream (ob->main_stream, len);
+
+  if (len)
+    {
+      unsigned spec_count = csum->speculative_call_targets.length ();
+      for (unsigned i = 0; i < spec_count; i++)
+	{
+	  speculative_call_target item = csum->speculative_call_targets[i];
+	  gcc_assert (item.target_id);
+	  streamer_write_hwi_stream (ob->main_stream, item.target_id);
+	  streamer_write_hwi_stream (ob->main_stream, item.target_probability);
+	}
+    }
+}
+
 /* Serialize the ipa info for lto.  */
 
 static void
@@ -238,10 +372,122 @@ ipa_profile_write_summary (void)
       streamer_write_uhwi_stream (ob->main_stream, histogram[i]->time);
       streamer_write_uhwi_stream (ob->main_stream, histogram[i]->size);
     }
+
+  if (!call_sums)
+    return;
+
+  /* Serialize speculative targets information.  */
+  unsigned int count = 0;
+  lto_symtab_encoder_t encoder = ob->decl_state->symtab_node_encoder;
+  lto_symtab_encoder_iterator lsei;
+  cgraph_node *node;
+
+  for (lsei = lsei_start_function_in_partition (encoder); !lsei_end_p (lsei);
+       lsei_next_function_in_partition (&lsei))
+    {
+      node = lsei_cgraph_node (lsei);
+      if (node->definition && node->has_gimple_body_p ()
+	  && node->indirect_calls)
+	count++;
+    }
+
+  streamer_write_uhwi_stream (ob->main_stream, count);
+
+  /* Process all of the functions.  */
+  for (lsei = lsei_start_function_in_partition (encoder);
+       !lsei_end_p (lsei) && count; lsei_next_function_in_partition (&lsei))
+    {
+      cgraph_node *node = lsei_cgraph_node (lsei);
+      if (node->definition && node->has_gimple_body_p ()
+	  && node->indirect_calls)
+	{
+	  int node_ref = lto_symtab_encoder_encode (encoder, node);
+	  streamer_write_uhwi_stream (ob->main_stream, node_ref);
+
+	  for (cgraph_edge *e = node->indirect_calls; e; e = e->next_callee)
+	    {
+	      speculative_call_summary *csum = call_sums->get_create (e);
+	      ipa_profile_write_edge_summary (ob, csum);
+	    }
+      }
+    }
+
   lto_destroy_simple_output_block (ob);
 }
 
-/* Deserialize the ipa info for lto.  */
+/* Dump all profile summary data for all cgraph nodes and edges to file F.  */
+
+static void
+ipa_profile_dump_all_summaries (FILE *f)
+{
+  fprintf (dump_file,
+	   "\n========== IPA-profile speculative targets: ==========\n");
+  cgraph_node *node;
+  FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
+    {
+      fprintf (f, "\nSummary for node %s:\n", node->dump_name ());
+      for (cgraph_edge *e = node->indirect_calls; e; e = e->next_callee)
+	{
+	  fprintf (f, "  Summary for %s of indirect edge %d:\n",
+		   e->caller->dump_name (), e->lto_stmt_uid);
+	  speculative_call_summary *csum = call_sums->get_create (e);
+	  csum->dump (f);
+	}
+    }
+  fprintf (f, "\n\n");
+}
+
+/* Read speculative targets information about edge for LTO WPA.  */
+
+static void
+ipa_profile_read_edge_summary (class lto_input_block *ib, cgraph_edge *edge)
+{
+  unsigned i, len;
+
+  len = streamer_read_hwi (ib);
+  gcc_assert (len <= GCOV_TOPN_VALUES);
+
+  speculative_call_summary *csum = call_sums->get_create (edge);
+
+  for (i = 0; i < len; i++)
+  {
+    speculative_call_target item (streamer_read_hwi (ib),
+	streamer_read_hwi (ib));
+    csum->speculative_call_targets.safe_push (item);
+  }
+}
+
+/* Read profile speculative targets section information for LTO WPA.  */
+
+static void
+ipa_profile_read_summary_section (struct lto_file_decl_data *file_data,
+				  class lto_input_block *ib)
+{
+  if (!ib)
+    return;
+
+  lto_symtab_encoder_t encoder = file_data->symtab_node_encoder;
+
+  unsigned int count = streamer_read_uhwi (ib);
+
+  unsigned int i;
+  unsigned int index;
+  cgraph_node * node;
+
+  for (i = 0; i < count; i++)
+    {
+      index = streamer_read_uhwi (ib);
+      encoder = file_data->symtab_node_encoder;
+      node
+	= dyn_cast<cgraph_node *> (lto_symtab_encoder_deref (encoder, index));
+
+      for (cgraph_edge *e = node->indirect_calls; e; e = e->next_callee)
+	ipa_profile_read_edge_summary (ib, e);
+    }
+}
+
+/* Deserialize the IPA histogram and speculative targets summary info for LTO.
+   */
 
 static void
 ipa_profile_read_summary (void)
@@ -253,6 +499,9 @@ ipa_profile_read_summary (void)
 
   hash_table<histogram_hash> hashtable (10);
 
+  gcc_checking_assert (!call_sums);
+  call_sums = new ipa_profile_call_summaries (symtab);
+
   while ((file_data = file_data_vec[j++]))
     {
       const char *data;
@@ -273,6 +522,9 @@ ipa_profile_read_summary (void)
 	      account_time_size (&hashtable, histogram,
 				 count, time, size);
 	    }
+
+	  ipa_profile_read_summary_section (file_data, ib);
+
 	  lto_destroy_simple_input_block (file_data,
 					  LTO_section_ipa_profile,
 					  ib, data, len);
@@ -512,6 +764,7 @@ ipa_profile (void)
   int nindirect = 0, ncommon = 0, nunknown = 0, nuseless = 0, nconverted = 0;
   int nmismatch = 0, nimpossible = 0;
   bool node_map_initialized = false;
+  gcov_type threshold;
 
   if (dump_file)
     dump_histogram (dump_file, histogram);
@@ -520,14 +773,12 @@ ipa_profile (void)
       overall_time += histogram[i]->count * histogram[i]->time;
       overall_size += histogram[i]->size;
     }
+  threshold = 0;
   if (overall_time)
     {
-      gcov_type threshold;
-
       gcc_assert (overall_size);
 
       cutoff = (overall_time * param_hot_bb_count_ws_permille + 500) / 1000;
-      threshold = 0;
       for (i = 0; cumulated < cutoff; i++)
 	{
 	  cumulated += histogram[i]->count * histogram[i]->time;
@@ -563,10 +814,21 @@ ipa_profile (void)
   histogram.release ();
   histogram_pool.release ();
 
-  /* Produce speculative calls: we saved common target from porfiling into
-     e->common_target_id.  Now, at link time, we can look up corresponding
+  /* Produce speculative calls: we saved common target from profiling into
+     e->target_id.  Now, at link time, we can look up corresponding
      function node and produce speculative call.  */
 
+  gcc_checking_assert (call_sums);
+
+  if (dump_file)
+    {
+      if (!node_map_initialized)
+	init_node_map (false);
+      node_map_initialized = true;
+
+      ipa_profile_dump_all_summaries (dump_file);
+    }
+
   FOR_EACH_DEFINED_FUNCTION (n)
     {
       bool update = false;
@@ -578,13 +840,35 @@ ipa_profile (void)
 	{
 	  if (n->count.initialized_p ())
 	    nindirect++;
-	  if (e->indirect_info->common_target_id)
+
+	  speculative_call_summary *csum = call_sums->get_create (e);
+	  unsigned spec_count = csum->speculative_call_targets.length ();
+	  if (spec_count)
 	    {
 	      if (!node_map_initialized)
-	        init_node_map (false);
+		init_node_map (false);
 	      node_map_initialized = true;
 	      ncommon++;
-	      n2 = find_func_by_profile_id (e->indirect_info->common_target_id);
+
+	      if (in_lto_p)
+		{
+		  if (dump_file)
+		    {
+		      fprintf (dump_file,
+			       "Updating hotness threshold in LTO mode.\n");
+		      fprintf (dump_file, "Updated min count: %" PRId64 "\n",
+			       (int64_t) threshold / spec_count);
+		    }
+		  set_hot_bb_threshold (threshold / spec_count);
+		}
+
+	      unsigned speculative_id = 0;
+	      bool speculative_found = false;
+	      for (unsigned i = 0; i < spec_count; i++)
+	      {
+	      speculative_call_target item
+		= csum->speculative_call_targets[i];
+	      n2 = find_func_by_profile_id (item.target_id);
 	      if (n2)
 		{
 		  if (dump_file)
@@ -593,11 +877,10 @@ ipa_profile (void)
 			       " other module %s => %s, prob %3.2f\n",
 			       n->dump_name (),
 			       n2->dump_name (),
-			       e->indirect_info->common_target_probability
-			       / (float)REG_BR_PROB_BASE);
+			       item.target_probability
+				 / (float) REG_BR_PROB_BASE);
 		    }
-		  if (e->indirect_info->common_target_probability
-		      < REG_BR_PROB_BASE / 2)
+		  if (item.target_probability < REG_BR_PROB_BASE / 2)
 		    {
 		      nuseless++;
 		      if (dump_file)
@@ -653,20 +936,26 @@ ipa_profile (void)
 			    n2 = alias;
 			}
 		      nconverted++;
-		      e->make_speculative
-			(n2,
-			 e->count.apply_probability
-				     (e->indirect_info->common_target_probability));
+		      e->make_speculative (n2,
+					   e->count.apply_probability (
+					     item.target_probability),
+					   speculative_id,
+					   item.target_probability);
 		      update = true;
+		      speculative_id++;
+		      speculative_found = true;
 		    }
 		}
 	      else
 		{
 		  if (dump_file)
 		    fprintf (dump_file, "Function with profile-id %i not found.\n",
-			     e->indirect_info->common_target_id);
+			     item.target_id);
 		  nunknown++;
 		}
+	       }
+	     if (speculative_found)
+	       e->indirect_info->num_speculative_call_targets = speculative_id;
 	    }
 	 }
        if (update)
@@ -729,6 +1018,10 @@ ipa_profile (void)
 	}
     }
   free (order);
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    symtab->dump (dump_file);
+
   return 0;
 }
 
diff --git a/gcc/ipa-ref.h b/gcc/ipa-ref.h
index f4ea7945338..1de5bd34b82 100644
--- a/gcc/ipa-ref.h
+++ b/gcc/ipa-ref.h
@@ -59,6 +59,9 @@ public:
   symtab_node *referred;
   gimple *stmt;
   unsigned int lto_stmt_uid;
+  /* speculative id is used to link direct calls with their corresponding
+     IPA_REF_ADDR references when representing speculative calls.  */
+  unsigned int speculative_id : 16;
   unsigned int referred_index;
   ENUM_BITFIELD (ipa_ref_use) use:3;
   unsigned int speculative:1;
diff --git a/gcc/ipa-utils.c b/gcc/ipa-utils.c
index 2e8298045fa..587ad5c6360 100644
--- a/gcc/ipa-utils.c
+++ b/gcc/ipa-utils.c
@@ -675,68 +675,133 @@ ipa_merge_profiles (struct cgraph_node *dst,
 	   e2 = (e2 ? e2->next_callee : NULL), e = e->next_callee)
 	{
 	  profile_count count = gimple_bb (e->call_stmt)->count;
+	  /* Below code are introduced by r279373 of "Fix merging of common
+	     traget info.".
+
+	     ipa-icf runs after ipa-profile, common_target_id with
+	     common_target_probablity are useless in ipa-icf since they are
+	     moved from cgraph.h to ipa-profile.c and processed already.
+	     Need double circulation to find out each mapped direct speculative
+	     edge and do prob merge.  Not easy to construct a case to cover all
+	     circumstances here.  For src and dst both have multiple speculative
+	     targets, only N:N maps are implemented, 2:0, 2:1, 0:2, 1:2 are not
+	     implemented yet as too complicated and no test cases to cover.  */
 	  if (copy_counts)
 	    {
-	      e->indirect_info->common_target_id
-		      = e2->indirect_info->common_target_id;
-	      e->indirect_info->common_target_probability
-		      = e2->indirect_info->common_target_probability;
+	      /* copy if both e and e2 have same num_speculative_call_targets.
+	       */
+	      if (e->num_speculative_call_targets_p ()
+		  == e2->num_speculative_call_targets_p ())
+		{
+		  int num_specs = e->num_speculative_call_targets_p ();
+		  cgraph_edge *direct, *indirect, *next_direct;
+		  cgraph_edge *direct2, *indirect2, *next_direct2;
+		  ipa_ref *ref;
+		  for (next_direct = e; next_direct && num_specs--;
+		       next_direct = direct->next_callee)
+		    {
+		      next_direct->speculative_call_info (direct, indirect,
+							  ref);
+
+		      int num_specs2 = e2->num_speculative_call_targets_p ();
+		      for (next_direct2 = e2; next_direct2 && num_specs2--;
+			   next_direct2 = direct2->next_callee)
+			{
+			  if (e2 && e2->speculative)
+			    next_direct2->speculative_call_info (direct2,
+								 indirect2,
+								 ref);
+			  if (direct->speculative_id == direct2->speculative_id
+			      && direct->lto_stmt_uid == direct2->lto_stmt_uid)
+			    {
+			      direct->target_prob = direct2->target_prob;
+			      break;
+			    }
+			}
+		    }
+		}
+	      else
+		gcc_assert (e->num_speculative_call_targets_p ()
+			    && e->num_speculative_call_targets_p ());
 	    }
-	  else if (e->indirect_info->common_target_id
-		   || e2->indirect_info->common_target_id)
+	  else if (e->num_speculative_call_targets_p ()
+		   || e2->num_speculative_call_targets_p ())
 	    {
-	      sreal scale1
-		 = e->count.ipa().to_sreal_scale (count);
-	      sreal scale2
-		 = e2->count.ipa().to_sreal_scale (count);
-
-	      if (scale1 == 0 && scale2 == 0)
-		scale1 = scale2 = 1;
-	      sreal sum = scale1 + scale2;
-	      int scaled_probability1
-		      = ((sreal)e->indirect_info->common_target_probability
-			* scale1 / sum).to_int ();
-	      int scaled_probability2
-		      = ((sreal)e2->indirect_info->common_target_probability
-			 * scale2 / sum).to_int ();
-	      if (symtab->dump_file)
+	      if (e->num_speculative_call_targets_p ()
+		  == e2->num_speculative_call_targets_p ())
 		{
-		  fprintf (symtab->dump_file,
-			   "Merging common targets %i prob %i"
-			   " and %i prob %i with scales %f %f\n",
-			   e->indirect_info->common_target_id,
-			   e->indirect_info->common_target_probability,
-			   e2->indirect_info->common_target_id,
-			   e2->indirect_info->common_target_probability,
-			   scale1.to_double (),
-			   scale2.to_double ());
-		  fprintf (symtab->dump_file, "Combined BB count ");
-		  count.dump (symtab->dump_file);
-		  fprintf (symtab->dump_file, " dst edge count ");
-		  e->count.dump (symtab->dump_file);
-		  fprintf (symtab->dump_file, " src edge count ");
-		  e2->count.dump (symtab->dump_file);
-		  fprintf (symtab->dump_file, "\n");
+		  int num_specs = e->num_speculative_call_targets_p ();
+		  cgraph_edge *direct, *indirect, *next_direct;
+		  cgraph_edge *direct2, *indirect2, *next_direct2;
+		  ipa_ref *ref;
+		  for (next_direct = e; next_direct && num_specs--;
+		       next_direct = direct->next_callee)
+		    {
+		      next_direct->speculative_call_info (direct, indirect,
+							  ref);
+
+		      int num_specs2 = e2->num_speculative_call_targets_p ();
+		      for (next_direct2 = e2; next_direct2 && num_specs2--;
+			   next_direct2 = direct2->next_callee)
+			{
+			  if (e2 && e2->speculative)
+			    next_direct2->speculative_call_info (direct2,
+								 indirect2,
+								 ref);
+			  if (direct->speculative_id == direct2->speculative_id
+			      && direct->lto_stmt_uid == direct2->lto_stmt_uid)
+			    {
+			      sreal scale1
+				= e->count.ipa ().to_sreal_scale (count);
+			      sreal scale2
+				= e2->count.ipa ().to_sreal_scale (count);
+
+			      if (scale1 == 0 && scale2 == 0)
+				scale1 = scale2 = 1;
+			      sreal sum = scale1 + scale2;
+			      int scaled_prob1
+				= (((sreal)direct->target_prob)
+				   * scale1 / sum).to_int ();
+			      int scaled_prob2
+				= (((sreal)direct2->target_prob)
+				   * scale2 / sum).to_int ();
+			      if (symtab->dump_file)
+				{
+				  fprintf (
+				    symtab->dump_file,
+				    "Merging speculative id %i prob %i"
+				    " and %i prob %i with scales %f %f\n",
+				    direct->speculative_id, direct->target_prob,
+				    direct2->speculative_id,
+				    direct2->target_prob, scale1.to_double (),
+				    scale2.to_double ());
+				  fprintf (symtab->dump_file,
+					   "Combined BB count ");
+				  count.dump (symtab->dump_file);
+				  fprintf (symtab->dump_file,
+					   " dst edge count ");
+				  e->count.dump (symtab->dump_file);
+				  fprintf (symtab->dump_file,
+					   " src edge count ");
+				  e2->count.dump (symtab->dump_file);
+				  fprintf (symtab->dump_file, "\n");
+				}
+			      direct->target_prob = scaled_prob1 + scaled_prob2;
+			      break;
+			    }
+			}
+		    }
 		}
-	      if (e->indirect_info->common_target_id
-		  == e2->indirect_info->common_target_id)
-		e->indirect_info->common_target_probability
-		       	= scaled_probability1 + scaled_probability2;
-	      else if (!e2->indirect_info->common_target_id
-		       || scaled_probability1 > scaled_probability2)
-		e->indirect_info->common_target_probability
-		       	= scaled_probability1;
-	      else 
+	      else if (e->num_speculative_call_targets_p ())
 		{
-		  e->indirect_info->common_target_id
-			  = e2->indirect_info->common_target_id;
-		  e->indirect_info->common_target_probability
-			  = scaled_probability2;
+		  /* Process if only dst is speculative.  */
+		  gcc_assert (!e->num_speculative_call_targets_p ());
+		}
+	      else if (e2->num_speculative_call_targets_p ())
+		{
+		  /* Process if only src is speculative.  */
+		  gcc_assert (!e2->num_speculative_call_targets_p ());
 		}
-	      if (symtab->dump_file)
-		fprintf (symtab->dump_file, "Merged as %i prob %i\n",
-			 e->indirect_info->common_target_id,
-			 e->indirect_info->common_target_probability);
 	    }
 
 	  /* When call is speculative, we need to re-distribute probabilities
diff --git a/gcc/lto-cgraph.c b/gcc/lto-cgraph.c
index dd605f65918..621607499e1 100644
--- a/gcc/lto-cgraph.c
+++ b/gcc/lto-cgraph.c
@@ -262,6 +262,7 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
   bp_pack_enum (&bp, cgraph_inline_failed_t,
 	        CIF_N_REASONS, edge->inline_failed);
   bp_pack_var_len_unsigned (&bp, uid);
+  bp_pack_value (&bp, edge->speculative_id, 16);
   bp_pack_value (&bp, edge->indirect_inlining_edge, 1);
   bp_pack_value (&bp, edge->speculative, 1);
   bp_pack_value (&bp, edge->call_stmt_cannot_inline_p, 1);
@@ -284,16 +285,11 @@ lto_output_edge (struct lto_simple_output_block *ob, struct cgraph_edge *edge,
 			     | ECF_SIBCALL
 			     | ECF_LEAF
 			     | ECF_NOVOPS)));
+
+      bp_pack_value (&bp, edge->indirect_info->num_speculative_call_targets,
+		     16);
     }
   streamer_write_bitpack (&bp);
-  if (edge->indirect_unknown_callee)
-    {
-      streamer_write_hwi_stream (ob->main_stream,
-			         edge->indirect_info->common_target_id);
-      if (edge->indirect_info->common_target_id)
-	streamer_write_hwi_stream
-	   (ob->main_stream, edge->indirect_info->common_target_probability);
-    }
 }
 
 /* Return if NODE contain references from other partitions.  */
@@ -690,6 +686,8 @@ lto_output_ref (struct lto_simple_output_block *ob, struct ipa_ref *ref,
       if (ref->stmt)
 	uid = gimple_uid (ref->stmt) + 1;
       streamer_write_hwi_stream (ob->main_stream, uid);
+      bp_pack_value (&bp, ref->speculative_id, 16);
+      streamer_write_bitpack (&bp);
     }
 }
 
@@ -1428,7 +1426,11 @@ input_ref (class lto_input_block *ib,
   ref = referring_node->create_reference (node, use);
   ref->speculative = speculative;
   if (is_a <cgraph_node *> (referring_node))
-    ref->lto_stmt_uid = streamer_read_hwi (ib);
+    {
+      ref->lto_stmt_uid = streamer_read_hwi (ib);
+      bp = streamer_read_bitpack (ib);
+      ref->speculative_id = bp_unpack_value (&bp, 16);
+    }
 }
 
 /* Read an edge from IB.  NODES points to a vector of previously read nodes for
@@ -1442,7 +1444,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
 {
   struct cgraph_node *caller, *callee;
   struct cgraph_edge *edge;
-  unsigned int stmt_id;
+  unsigned int stmt_id, speculative_id;
   profile_count count;
   cgraph_inline_failed_t inline_failed;
   struct bitpack_d bp;
@@ -1466,6 +1468,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
   bp = streamer_read_bitpack (ib);
   inline_failed = bp_unpack_enum (&bp, cgraph_inline_failed_t, CIF_N_REASONS);
   stmt_id = bp_unpack_var_len_unsigned (&bp);
+  speculative_id = bp_unpack_value (&bp, 16);
 
   if (indirect)
     edge = caller->create_indirect_edge (NULL, 0, count);
@@ -1475,6 +1478,7 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
   edge->indirect_inlining_edge = bp_unpack_value (&bp, 1);
   edge->speculative = bp_unpack_value (&bp, 1);
   edge->lto_stmt_uid = stmt_id;
+  edge->speculative_id = speculative_id;
   edge->inline_failed = inline_failed;
   edge->call_stmt_cannot_inline_p = bp_unpack_value (&bp, 1);
   edge->can_throw_external = bp_unpack_value (&bp, 1);
@@ -1494,9 +1498,9 @@ input_edge (class lto_input_block *ib, vec<symtab_node *> nodes,
       if (bp_unpack_value (&bp, 1))
 	ecf_flags |= ECF_RETURNS_TWICE;
       edge->indirect_info->ecf_flags = ecf_flags;
-      edge->indirect_info->common_target_id = streamer_read_hwi (ib);
-      if (edge->indirect_info->common_target_id)
-        edge->indirect_info->common_target_probability = streamer_read_hwi (ib);
+
+      edge->indirect_info->num_speculative_call_targets
+	= bp_unpack_value (&bp, 16);
     }
 }
 
diff --git a/gcc/predict.c b/gcc/predict.c
index a134deb9b3f..02c5fe0667d 100644
--- a/gcc/predict.c
+++ b/gcc/predict.c
@@ -761,7 +761,6 @@ dump_prediction (FILE *file, enum br_predictor predictor, int probability,
       && bb->count.precise_p ()
       && reason == REASON_NONE)
     {
-      gcc_assert (e->count ().precise_p ());
       fprintf (file, ";;heuristics;%s;%" PRId64 ";%" PRId64 ";%.1f;\n",
 	       predictor_info[predictor].name,
 	       bb->count.to_gcov_type (), e->count ().to_gcov_type (),
diff --git a/gcc/symtab.c b/gcc/symtab.c
index 1b1b0ee409d..f141200a452 100644
--- a/gcc/symtab.c
+++ b/gcc/symtab.c
@@ -605,6 +605,7 @@ symtab_node::create_reference (symtab_node *referred_node,
   ref->referred = referred_node;
   ref->stmt = stmt;
   ref->lto_stmt_uid = 0;
+  ref->speculative_id = 0;
   ref->use = use_type;
   ref->speculative = 0;
 
@@ -662,6 +663,7 @@ symtab_node::clone_references (symtab_node *node)
       ref2 = create_reference (ref->referred, ref->use, ref->stmt);
       ref2->speculative = speculative;
       ref2->lto_stmt_uid = stmt_uid;
+      ref2->speculative_id = ref->speculative_id;
     }
 }
 
@@ -680,6 +682,7 @@ symtab_node::clone_referring (symtab_node *node)
       ref2 = ref->referring->create_reference (this, ref->use, ref->stmt);
       ref2->speculative = speculative;
       ref2->lto_stmt_uid = stmt_uid;
+      ref2->speculative_id = ref->speculative_id;
     }
 }
 
@@ -695,6 +698,7 @@ symtab_node::clone_reference (ipa_ref *ref, gimple *stmt)
   ref2 = create_reference (ref->referred, ref->use, stmt);
   ref2->speculative = speculative;
   ref2->lto_stmt_uid = stmt_uid;
+  ref2->speculative_id = ref->speculative_id;
   return ref2;
 }
 
@@ -749,6 +753,7 @@ symtab_node::clear_stmts_in_references (void)
       {
 	r->stmt = NULL;
 	r->lto_stmt_uid = 0;
+	r->speculative_id = 0;
       }
 }
 
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index cccc2853ed5..9a62668c548 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,13 @@
+2020-01-13  Xiong Hu Luo  <luoxhu@linux.ibm.com>
+
+	PR ipa/69678
+	* gcc.dg/tree-prof/indir-call-prof-topn.c: New testcase.
+	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c: New testcase.
+	* gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c: New testcase.
+	* gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c: New testcase.
+	* lib/scandump.exp: Dump executable file name.
+	* lib/scanwpaipa.exp: New scan-pgo-wap-ipa-dump.
+
 2020-01-10  Thomas Schwinge  <thomas@codesourcery.com>
 
 	* c-c++-common/goacc/host_data-1.c: Extend.
diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
new file mode 100644
index 00000000000..a13b08cd60e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
@@ -0,0 +1,33 @@
+/* { dg-require-effective-target lto } */
+/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate" } */
+
+#include <stdio.h>
+
+typedef int (*fptr) (int);
+int
+one (int a);
+
+int
+two (int a);
+
+fptr table[] = {&one, &two};
+
+int
+main()
+{
+  int i, x;
+  fptr p = &one;
+
+  x = one (3);
+
+  for (i = 0; i < 350000000; i++)
+    {
+      x = (*p) (3);
+      p = table[x];
+    }
+  printf ("done:%d\n", x);
+}
+
+/* { dg-final-use-not-autofdo { scan-pgo-wpa-ipa-dump "2 \\(200.00%\\) speculations produced." "profile_estimate" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
new file mode 100644
index 00000000000..a8c6e365fb9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c
@@ -0,0 +1,22 @@
+/* It seems there is no way to avoid the other source of mulitple
+   source testcase from being compiled independently.  Just avoid
+   error.  */
+#ifdef DOJOB
+int
+one (int a)
+{
+  return 1;
+}
+
+int
+two (int a)
+{
+  return 0;
+}
+#else
+int
+main()
+{
+  return 0;
+}
+#endif
diff --git a/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
new file mode 100644
index 00000000000..9b996fcf0ed
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
@@ -0,0 +1,40 @@
+/* { dg-require-effective-target lto } */
+/* { dg-additional-sources "crossmodule-indir-call-topn-1a.c" } */
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-options "-O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate" } */
+
+#include <stdio.h>
+
+typedef int (*fptr) (int);
+int
+one (int a);
+
+int
+two (int a);
+
+fptr table[] = {&one, &two};
+
+int foo ()
+{
+  int i, x;
+  fptr p = &one;
+
+  x = one (3);
+
+  for (i = 0; i < 350000000; i++)
+    {
+      x = (*p) (3);
+      p = table[x];
+    }
+  return x;
+}
+
+int
+main()
+{
+  int x = foo ();
+  printf ("done:%d\n", x);
+}
+
+/* { dg-final-use-not-autofdo { scan-pgo-wpa-ipa-dump "2 \\(200.00%\\) speculations produced." "profile_estimate" } } */
+
diff --git a/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
new file mode 100644
index 00000000000..063996c71df
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-prof/indir-call-prof-topn.c
@@ -0,0 +1,37 @@
+/* { dg-require-profiling "-fprofile-generate" } */
+/* { dg-options "-O2 -fdump-ipa-profile_estimate" } */
+
+#include <stdio.h>
+
+typedef int (*fptr) (int);
+int
+one (int a)
+{
+  return 1;
+}
+
+int
+two (int a)
+{
+  return 0;
+}
+
+fptr table[] = {&one, &two};
+
+int
+main()
+{
+  int i, x;
+  fptr p = &one;
+
+  one (3);
+
+  for (i = 0; i < 350000000; i++)
+    {
+      x = (*p) (3);
+      p = table[x];
+    }
+  printf ("done:%d\n", x);
+}
+
+/* { dg-final-use-not-autofdo { scan-ipa-dump "2 \\(200.00%\\) speculations produced." "profile_estimate" } } */
diff --git a/gcc/testsuite/lib/scandump.exp b/gcc/testsuite/lib/scandump.exp
index 4c7d904cf77..d6ba350acc8 100644
--- a/gcc/testsuite/lib/scandump.exp
+++ b/gcc/testsuite/lib/scandump.exp
@@ -70,6 +70,7 @@ proc scan-dump { args } {
     set output_file "[glob -nocomplain $dumpbase.[lindex $args 2]]"
     if { $output_file == "" } {
 	verbose -log "$testcase: dump file does not exist"
+	verbose -log "dump file: $dumpbase.$suf"
 	unresolved "$testname"
 	return
     }
diff --git a/gcc/testsuite/lib/scanwpaipa.exp b/gcc/testsuite/lib/scanwpaipa.exp
index d00b400dd42..2f58823a4f3 100644
--- a/gcc/testsuite/lib/scanwpaipa.exp
+++ b/gcc/testsuite/lib/scanwpaipa.exp
@@ -45,6 +45,29 @@ proc scan-wpa-ipa-dump { args } {
     }
 }
 
+# Argument 0 is the regexp to match
+# Argument 1 is the name of the dumped ipa pass
+# Argument 2 handles expected failures and the like
+proc scan-pgo-wpa-ipa-dump { args } {
+
+    if { [llength $args] < 2 } {
+	error "scan-pgo-wpa-ipa-dump: too few arguments"
+	return
+    }
+    if { [llength $args] > 3 } {
+	error "scan-pgo-wpa-ipa-dump: too many arguments"
+	return
+    }
+    if { [llength $args] >= 3 } {
+	scan-dump "pgo-wpa-ipa" [lindex $args 0] \
+		  "\[0-9\]\[0-9\]\[0-9\]i.[lindex $args 1]" ".x02.wpa" \
+		  [lindex $args 2]
+    } else {
+	scan-dump "pgo-wpa-ipa" [lindex $args 0] \
+		  "\[0-9\]\[0-9\]\[0-9\]i.[lindex $args 1]" ".x02.wpa"
+    }
+}
+
 # Call pass if pattern is present given number of times, otherwise fail.
 # Argument 0 is the regexp to match
 # Argument 1 is number of times the regexp must be found
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index 51aa06566b1..2197769bf17 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -2187,9 +2187,10 @@ copy_bb (copy_body_data *id, basic_block bb,
 					  num, den,
 					  true);
 
-		      /* Speculative calls consist of two edges - direct and
-			 indirect.  Duplicate the whole thing and distribute
-			 frequencies accordingly.  */
+		      /* A speculative call is consist of edges - indirect edge
+			 and direct edges (one indirect edeg may has multiple
+			 direct edges).  Duplicate the whole thing and
+			 distribute frequencies accordingly.  */
 		      if (edge->speculative)
 			{
 			  struct cgraph_edge *direct, *indirect;
@@ -2197,8 +2198,33 @@ copy_bb (copy_body_data *id, basic_block bb,
 
 			  gcc_assert (!edge->indirect_unknown_callee);
 			  old_edge->speculative_call_info (direct, indirect, ref);
+			  while (old_edge->next_callee
+				 && old_edge->next_callee->speculative
+				 && indirect->num_speculative_call_targets_p ()
+				      > 1)
+			    {
+			      id->dst_node->clone_reference (ref, stmt);
+
+			      edge = old_edge->next_callee;
+			      edge = edge->clone (id->dst_node, call_stmt,
+						  gimple_uid (stmt), num, den,
+						  true);
+			      old_edge = old_edge->next_callee;
+			      gcc_assert (!edge->indirect_unknown_callee);
+
+			      /* If the indirect edge has multiple speculative
+				 calls, iterate through all direct calls
+				 associated to the speculative call and clone
+				 all related direct edges before cloning the
+				 related indirect edge.  */
+			      old_edge->speculative_call_info (direct, indirect,
+							       ref);
+			    }
 
 			  profile_count indir_cnt = indirect->count;
+
+			  /* Duplicate the indirect edge after all direct edges
+			     cloned.  */
 			  indirect = indirect->clone (id->dst_node, call_stmt,
 						      gimple_uid (stmt),
 						      num, den,
diff --git a/gcc/value-prof.c b/gcc/value-prof.c
index 81994964148..9a2c46252c6 100644
--- a/gcc/value-prof.c
+++ b/gcc/value-prof.c
@@ -106,7 +106,7 @@ static bool gimple_divmod_fixed_value_transform (gimple_stmt_iterator *);
 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 gimple_ic_transform (gimple_stmt_iterator *);
 
 /* Allocate histogram value.  */
 
@@ -616,8 +616,7 @@ gimple_value_profile_transformations (void)
 	  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;
@@ -628,6 +627,9 @@ gimple_value_profile_transformations (void)
 		  gsi = gsi_for_stmt (stmt);
 		}
 	    }
+
+	  /* The function never thansforms a GIMPLE statement.  */
+	  gimple_ic_transform (&gsi);
         }
     }
 
@@ -1386,13 +1388,12 @@ gimple_ic (gcall *icall_stmt, struct cgraph_node *direct_call,
   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:
- */
+/* There maybe multiple indirect targets in histogram.  Check every
+   indirect/virtual call if callee function exists, if not exist, leave it to
+   LTO stage for later process.  Modify code of this indirect call to an if-else
+   structure in ipa-profile finally.  */
 
-static bool
+static void
 gimple_ic_transform (gimple_stmt_iterator *gsi)
 {
   gcall *stmt;
@@ -1402,52 +1403,58 @@ gimple_ic_transform (gimple_stmt_iterator *gsi)
 
   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;
+    return;
 
-  if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
-				  &count, &all))
-    return false;
+  count = 0;
+  all = histogram->hvalue.counters[0];
 
-  if (4 * count <= 3 * all)
-    return false;
+  for (unsigned j = 0; j < GCOV_TOPN_VALUES; j++)
+    {
+      if (!get_nth_most_common_value (NULL, "indirect call", histogram, &val,
+				      &count, &all, j))
+	return;
 
-  direct_call = find_func_by_profile_id ((int)val);
+      /* Minimum probability.  should be higher than 25%.  */
+      if (4 * count <= all)
+	return;
 
-  if (direct_call == NULL)
-    {
-      if (val)
+      direct_call = find_func_by_profile_id ((int) val);
+
+      if (direct_call == NULL)
 	{
-	  if (dump_enabled_p ())
-	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, stmt,
-			     "Indirect call -> direct call from other "
-			     "module %T=> %i (will resolve only with LTO)\n",
-			     gimple_call_fn (stmt), (int)val);
+	  if (val)
+	    {
+	      if (dump_enabled_p ())
+		dump_printf_loc (
+		  MSG_MISSED_OPTIMIZATION, stmt,
+		  "Indirect call -> direct call from other "
+		  "module %T=> %i (will resolve only with LTO)\n",
+		  gimple_call_fn (stmt), (int) val);
+	    }
+	  return;
 	}
-      return false;
-    }
 
-  if (dump_enabled_p ())
-    {
-      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
-		       "Indirect call -> direct call "
-		       "%T => %T transformation on insn postponed\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_enabled_p ())
+	{
+	  dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, stmt,
+			   "Indirect call -> direct call "
+			   "%T => %T transformation on insn postponed\n",
+			   gimple_call_fn (stmt), direct_call->decl);
+	  dump_printf_loc (MSG_NOTE, stmt,
+			   "hist->count %" PRId64 " hist->all %" PRId64 "\n",
+			   count, all);
+	}
     }
-
-  return true;
 }
 
 /* Return true if the stringop CALL shall be profiled.  SIZE_ARG be
diff --git a/gcc/value-prof.h b/gcc/value-prof.h
index bf024a3854c..81c615dadec 100644
--- a/gcc/value-prof.h
+++ b/gcc/value-prof.h
@@ -89,7 +89,6 @@ void verify_histograms (void);
 void free_histograms (function *);
 void stringop_block_profile (gimple *, unsigned int *, HOST_WIDE_INT *);
 gcall *gimple_ic (gcall *, struct cgraph_node *, profile_probability);
-bool check_ic_target (gcall *, struct cgraph_node *);
 bool get_nth_most_common_value (gimple *stmt, const char *counter_type,
 				histogram_value hist, gcov_type *value,
 				gcov_type *count, gcov_type *all,
-- 
2.21.0.777.g83232e3864


^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v7] Missed function specialization + partial devirtualization
  2020-01-13  8:47                                       ` luoxhu
@ 2020-01-14 13:37                                         ` Martin Liška
  2020-01-14 14:55                                           ` luoxhu
  2020-01-14 20:14                                         ` David Malcolm
  1 sibling, 1 reply; 38+ messages in thread
From: Martin Liška @ 2020-01-14 13:37 UTC (permalink / raw)
  To: luoxhu, Jan Hubicka; +Cc: gcc-patches, segher, wschmidt

On 1/13/20 4:23 AM, luoxhu wrote:
> Thanks a lot!  Rebased & updated, will commit below patch shortly when git push is ready.

Hello.

I'm pretty sure the patch contains failure of the following tests:

FAIL: gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c scan-pgo-wpa-ipa-dump profile_estimate "2 \\(200.00%\\) speculations produced."
FAIL: gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c scan-pgo-wpa-ipa-dump profile_estimate "2 \\(200.00%\\) speculations produced."
FAIL: gcc.dg/tree-prof/crossmodule-indircall-1.c execution,    -fprofile-use -D_PROFILE_USE

Can you please take a look?
Thanks,
Martin

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v7] Missed function specialization + partial devirtualization
  2020-01-14 13:37                                         ` Martin Liška
@ 2020-01-14 14:55                                           ` luoxhu
  2020-01-15 10:44                                             ` Christophe Lyon
  0 siblings, 1 reply; 38+ messages in thread
From: luoxhu @ 2020-01-14 14:55 UTC (permalink / raw)
  To: Martin Liška, Jan Hubicka; +Cc: gcc-patches, segher, wschmidt

[-- Attachment #1: Type: text/plain, Size: 1154 bytes --]

Hi,

On 2020/1/14 21:03, Martin Liška wrote:
> On 1/13/20 4:23 AM, luoxhu wrote:
>> Thanks a lot!  Rebased & updated, will commit below patch shortly when 
>> git push is ready.
> 
> Hello.
> 
> I'm pretty sure the patch contains failure of the following tests:
> 
> FAIL: gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c 
> scan-pgo-wpa-ipa-dump profile_estimate "2 \\(200.00%\\) speculations 
> produced."
> FAIL: gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c 
> scan-pgo-wpa-ipa-dump profile_estimate "2 \\(200.00%\\) speculations 
> produced."
> FAIL: gcc.dg/tree-prof/crossmodule-indircall-1.c execution,    
> -fprofile-use -D_PROFILE_USE
> 
> Can you please take a look?

Thanks for reporting.
This seems fails only on some platforms.  I tested it before commit and
just verified it again on my Power8-LE with gcc.sum andd log attached. 
I suppose you are trying on x86 platforms and sorry that I don't have one
at hand, these failures should be easy to fix, If not, I will investigate
it after returning from Chinese New Year vacation. 

make check-gcc RUNTESTFLAGS="tree-prof.exp=crossmodule-* -v -v" > log

> Thanks,
> Martin

[-- Attachment #2: gcc.sum --]
[-- Type: text/plain, Size: 3119 bytes --]

Test Run By luoxhu on Tue Jan 14 08:22:32 2020
Native configuration is powerpc64le-unknown-linux-gnu

		=== gcc tests ===

Schedule of variations:
    unix

Running target unix
Running /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/tree-prof.exp ...
PASS: gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c compilation,  -fprofile-generate -D_PROFILE_GENERATE
PASS: gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c execution,    -fprofile-generate -D_PROFILE_GENERATE
PASS: gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c compilation,  -fprofile-use -D_PROFILE_USE
PASS: gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c execution,    -fprofile-use -D_PROFILE_USE
PASS: gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c scan-pgo-wpa-ipa-dump profile_estimate "2 \\(200.00%\\) speculations produced."
PASS: gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c compilation,  -fprofile-generate -D_PROFILE_GENERATE
PASS: gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c execution,    -fprofile-generate -D_PROFILE_GENERATE
PASS: gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c compilation,  -fprofile-use -D_PROFILE_USE
PASS: gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c execution,    -fprofile-use -D_PROFILE_USE
PASS: gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c compilation,  -fprofile-generate -D_PROFILE_GENERATE
PASS: gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c execution,    -fprofile-generate -D_PROFILE_GENERATE
PASS: gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c compilation,  -fprofile-use -D_PROFILE_USE
PASS: gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c execution,    -fprofile-use -D_PROFILE_USE
PASS: gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c scan-pgo-wpa-ipa-dump profile_estimate "2 \\(200.00%\\) speculations produced."
PASS: gcc.dg/tree-prof/crossmodule-indircall-1.c compilation,  -fprofile-generate -D_PROFILE_GENERATE
PASS: gcc.dg/tree-prof/crossmodule-indircall-1.c execution,    -fprofile-generate -D_PROFILE_GENERATE
PASS: gcc.dg/tree-prof/crossmodule-indircall-1.c compilation,  -fprofile-use -D_PROFILE_USE
PASS: gcc.dg/tree-prof/crossmodule-indircall-1.c execution,    -fprofile-use -D_PROFILE_USE
PASS: gcc.dg/tree-prof/crossmodule-indircall-1a.c compilation,  -fprofile-generate -D_PROFILE_GENERATE
PASS: gcc.dg/tree-prof/crossmodule-indircall-1a.c execution,    -fprofile-generate -D_PROFILE_GENERATE
PASS: gcc.dg/tree-prof/crossmodule-indircall-1a.c compilation,  -fprofile-use -D_PROFILE_USE
PASS: gcc.dg/tree-prof/crossmodule-indircall-1a.c execution,    -fprofile-use -D_PROFILE_USE
UNSUPPORTED: gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c -fauto-profile
UNSUPPORTED: gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c -fauto-profile
UNSUPPORTED: gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c -fauto-profile
UNSUPPORTED: gcc.dg/tree-prof/crossmodule-indircall-1.c -fauto-profile
UNSUPPORTED: gcc.dg/tree-prof/crossmodule-indircall-1a.c -fauto-profile

		=== gcc Summary ===

# of expected passes		22
# of unsupported tests		5
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc  version 10.0.0 20200114 (experimental) (GCC) 


[-- Attachment #3: log --]
[-- Type: text/plain, Size: 192903 bytes --]

make[1]: Entering directory '/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc'
rm -rf testsuite/gcc-parallel
make[2]: Entering directory '/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc'
(rootme=`${PWDCMD-pwd}`; export rootme; \
srcdir=`cd ../../gcc-trunk/gcc; ${PWDCMD-pwd}` ; export srcdir ; \
if [ -n "" ] \
   && [ -n "$GCC_RUNTEST_PARALLELIZE_DIR" ] \
   && [ -f testsuite/gcc-parallel/finished ]; then \
  rm -rf testsuite/gcc; \
else \
  cd testsuite/gcc; \
  rm -f tmp-site.exp; \
  sed '/set tmpdir/ s|testsuite$|testsuite/gcc|' \
	< ../../site.exp > tmp-site.exp; \
  /bin/sh ${srcdir}/../move-if-change tmp-site.exp site.exp; \
  EXPECT=`if [ -f ${rootme}/../expect/expect ] ; then echo ${rootme}/../expect/expect ; else echo expect ; fi` ; export EXPECT ; \
  if [ -f ${rootme}/../expect/expect ] ; then  \
    TCL_LIBRARY=`cd .. ; cd ${srcdir}/../tcl/library ; ${PWDCMD-pwd}` ; \
    export TCL_LIBRARY ; \
  fi ; \
  `if [ -f ${srcdir}/../dejagnu/runtest ] ; then echo ${srcdir}/../dejagnu/runtest ; else echo runtest; fi` --tool gcc tree-prof.exp=crossmodule-* -v -v; \
  if [ -n "$GCC_RUNTEST_PARALLELIZE_DIR" ] ; then \
    touch ${rootme}/testsuite/gcc-parallel/finished; \
  fi ; \
fi )
Expect binary is expect
Looking for /usr/share/dejagnu/runtest.exp.
Using /usr/share/dejagnu/runtest.exp as main test driver
Verbose level is 2
Login name is luoxhu
Looking for ~/.dejagnurc
Looking for /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/site.exp
Found /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/site.exp
Using test sources in /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite
Using test binaries in /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc
Tool root directory is /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite
Using /usr/share/dejagnu to find libraries
Looking for /usr/share/dejagnu/site.exp
Found /usr/share/dejagnu/site.exp
Looking for library file ../lib/utils.exp
Looking for library file /usr/share/dejagnu/utils.exp
Loading /usr/share/dejagnu/utils.exp
Looking for library file ../lib/framework.exp
Looking for library file /usr/share/dejagnu/framework.exp
Loading /usr/share/dejagnu/framework.exp
Looking for library file ../lib/debugger.exp
Looking for library file /usr/share/dejagnu/debugger.exp
Loading /usr/share/dejagnu/debugger.exp
Looking for library file ../lib/remote.exp
Looking for library file /usr/share/dejagnu/remote.exp
Loading /usr/share/dejagnu/remote.exp
Looking for library file ../lib/telnet.exp
Looking for library file /usr/share/dejagnu/telnet.exp
Loading /usr/share/dejagnu/telnet.exp
Looking for library file ../lib/rlogin.exp
Looking for library file /usr/share/dejagnu/rlogin.exp
Loading /usr/share/dejagnu/rlogin.exp
Looking for library file ../lib/kermit.exp
Looking for library file /usr/share/dejagnu/kermit.exp
Loading /usr/share/dejagnu/kermit.exp
Looking for library file ../lib/tip.exp
Looking for library file /usr/share/dejagnu/tip.exp
Loading /usr/share/dejagnu/tip.exp
Looking for library file ../lib/rsh.exp
Looking for library file /usr/share/dejagnu/rsh.exp
Loading /usr/share/dejagnu/rsh.exp
Looking for library file ../lib/ftp.exp
Looking for library file /usr/share/dejagnu/ftp.exp
Loading /usr/share/dejagnu/ftp.exp
Looking for library file ../lib/target.exp
Looking for library file /usr/share/dejagnu/target.exp
Loading /usr/share/dejagnu/target.exp
Looking for library file ../lib/targetdb.exp
Looking for library file /usr/share/dejagnu/targetdb.exp
Loading /usr/share/dejagnu/targetdb.exp
Looking for library file ../lib/libgloss.exp
Looking for library file /usr/share/dejagnu/libgloss.exp
Loading /usr/share/dejagnu/libgloss.exp
Loading library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/gcc.exp
Looking for library file ../lib/prune.exp
Looking for library file /usr/share/dejagnu/prune.exp
Looking for library file /usr/share/dejagnu/lib/prune.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/prune.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/prune.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/prune.exp
Looking for library file ../lib/multiline.exp
Looking for library file /usr/share/dejagnu/multiline.exp
Looking for library file /usr/share/dejagnu/lib/multiline.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/multiline.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/multiline.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/multiline.exp
Looking for library file ../lib/gcc-defs.exp
Looking for library file /usr/share/dejagnu/gcc-defs.exp
Looking for library file /usr/share/dejagnu/lib/gcc-defs.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/gcc-defs.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/gcc-defs.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/gcc-defs.exp
Looking for library file ../lib/target-libpath.exp
Looking for library file /usr/share/dejagnu/target-libpath.exp
Looking for library file /usr/share/dejagnu/lib/target-libpath.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/target-libpath.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/target-libpath.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/target-libpath.exp
Looking for library file ../lib/wrapper.exp
Looking for library file /usr/share/dejagnu/wrapper.exp
Looking for library file /usr/share/dejagnu/lib/wrapper.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/wrapper.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/wrapper.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/wrapper.exp
Looking for library file ../lib/target-utils.exp
Looking for library file /usr/share/dejagnu/target-utils.exp
Looking for library file /usr/share/dejagnu/lib/target-utils.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/target-utils.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/target-utils.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/target-utils.exp
Looking for library file ../lib/target-supports.exp
Looking for library file /usr/share/dejagnu/target-supports.exp
Looking for library file /usr/share/dejagnu/lib/target-supports.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/target-supports.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/target-supports.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/target-supports.exp
Looking for library file ../lib/timeout.exp
Looking for library file /usr/share/dejagnu/timeout.exp
Looking for library file /usr/share/dejagnu/lib/timeout.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/timeout.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/timeout.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/timeout.exp
Testing gcc
Running only tests tree-prof.exp=crossmodule-*
Opening log files in .
Test Run By luoxhu on Tue Jan 14 08:22:32 2020
Checking powerpc64le-unknown-linux-gnu against powerpc64le-unknown-linux-gnu
Native configuration is powerpc64le-unknown-linux-gnu

		=== gcc tests ===

setting trap for SIGTERM to terminated
setting trap for SIGINT to interrupted by user
setting trap for SIGQUIT to interrupted by user
dirlist is /usr/share/dejagnu/baseboards
Looking for standard board description file for build /usr/share/dejagnu/baseboards/standard.exp
Looking for board description file for build /usr/share/dejagnu/baseboards/genoa.exp
pushing config for build, name is genoa
dirlist is /usr/share/dejagnu/baseboards
Looking for standard board description file for host /usr/share/dejagnu/baseboards/standard.exp
Looking for board description file for host /usr/share/dejagnu/baseboards/genoa.exp
pushing config for host, name is genoa
Schedule of variations:
    unix

target is unix
Running target unix
Looking for tool-and-target-specific interface file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/config/base-config.exp
Looking for tool-and-target-specific interface file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/../config/base-config.exp
Looking for tool-and-target-specific interface file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/../../config/base-config.exp
Looking for tool-and-target-specific interface file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/../../../config/base-config.exp
dirlist is /usr/share/dejagnu/baseboards/genoa /usr/share/dejagnu/baseboards
Looking for standard board description file for target /usr/share/dejagnu/baseboards/genoa/standard.exp
Looking for standard board description file for target /usr/share/dejagnu/baseboards/standard.exp
Looking for board description file for target /usr/share/dejagnu/baseboards/genoa/unix.exp
Looking for board description file for target /usr/share/dejagnu/baseboards/unix.exp
Using /usr/share/dejagnu/baseboards/unix.exp as board description file for target.
Looking for generic interface file for target /usr/share/dejagnu/config/unix.exp
Using /usr/share/dejagnu/config/unix.exp as generic interface file for target.
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/./xgcc
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/../xgcc
Checking "*-*-cygwin*" against "powerpc64le-unknown-linux-gnu"
Checking "alpha*-*" against "powerpc64le-unknown-linux-gnu"
Checking "sparc64-*-linux-gnu" against "powerpc64le-unknown-linux-gnu"
Looking for tool-and-target-specific interface file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/config/unix.exp
Looking for tool-and-target-specific interface file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/config/gnu.exp
Looking for tool-and-target-specific interface file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/config/default.exp
Using /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/config/default.exp as tool-and-target-specific interface file.
Looking for library file ../lib/standard.exp
Looking for library file /usr/share/dejagnu/standard.exp
Loading /usr/share/dejagnu/standard.exp
pushing config for target, name is unix
Top level testsuite dirs are /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.c-torture /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg-selftests /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.misc-tests /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.src /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.test-framework
trying to glob /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.c-torture/compile/compile.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.c-torture/execute/builtins/builtins.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.c-torture/execute/execute.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.c-torture/execute/ieee/ieee.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.c-torture/unsorted/unsorted.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/asan/asan.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/atomic/atomic.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/autopar/autopar.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/charset/charset.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/compat/compat.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/compat/struct-layout-1.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/cpp/cpp.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/cpp/trad/trad.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/debug/debug.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf2.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/dfp/dfp.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/dg.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/fixed-point/fixed-point.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/format/format.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/goacc-gomp/goacc-gomp.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/goacc/goacc.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/gomp/gomp.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/graphite/graphite.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/guality/guality.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/ipa/ipa.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/lto/lto.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/noncompile/noncompile.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/params/params.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/pch/pch.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/plugin/plugin.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/rtl/rtl.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/sancov/sancov.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/simulate-thread/simulate-thread.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/special/mips-abi.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/special/special.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/sso/sso.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tls/tls.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tm/tm.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/torture/dg-torture.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/torture/stackalign/stackalign.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/torture/tls/tls.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/tree-prof.exp in tree-prof.exp
Running /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/tree-prof.exp ...
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/./xgcc
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/../xgcc
Profiling argument is <-fprofile-generate>
Checking "powerpc-ibm-aix*" against "powerpc64le-unknown-linux-gnu"
check_cached_effective_target tls_runtime: checking unix
check_compile tool: gcc for tls_runtime
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ tls_runtime115344.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never    -lm  -o tls_runtime115344.exe
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ tls_runtime115344.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never    -lm  -o tls_runtime115344.exe    (timeout = 300)
pid is 115366 -115366
waitres is 115366 exp6 0 0
output is  status 0
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
loading to unix
Setting LD_LIBRARY_PATH to :/home/luoxhu/local/lib/
spawning command ./tls_runtime115344.exe 
exp9 file6
Executed ./tls_runtime115344.exe, status 0
check_runtime_nocache tls_runtime: status is <pass>
check_cached_effective_target tls_runtime: returning 1 for unix
Checking "*-*-solaris2*" against "powerpc64le-unknown-linux-gnu"
Checking "mips*-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-mingw*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-cygwin*" against "powerpc64le-unknown-linux-gnu"
check_cached_effective_target uclibc: checking unix
check_compile tool: gcc for uclibc
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -c -o uclibc115344.o uclibc115344.c
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -c -o uclibc115344.o uclibc115344.c    (timeout = 300)
pid is 115379 -115379
waitres is 115379 exp6 0 1
close result is 115379 exp6 0 1
output is uclibc115344.c:4:3: error: #error !__UCLIBC__
 status 1
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
compiler exited with status 1
output is:
uclibc115344.c:4:3: error: #error !__UCLIBC__

check_cached_effective_target uclibc: returning 0 for unix
check_cached_effective_target profiling_available: checking unix
Checking "aarch64*-*-elf" against "powerpc64le-unknown-linux-gnu"
Checking "am3*-*-linux*" against "powerpc64le-unknown-linux-gnu"
Checking "amdgcn-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "arm*-*-eabi*" against "powerpc64le-unknown-linux-gnu"
Checking "arm*-*-elf" against "powerpc64le-unknown-linux-gnu"
Checking "arm*-*-symbianelf*" against "powerpc64le-unknown-linux-gnu"
Checking "avr-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "bfin-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "cris-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "crisv32-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "csky-*-elf" against "powerpc64le-unknown-linux-gnu"
Checking "fido-*-elf" against "powerpc64le-unknown-linux-gnu"
Checking "h8300-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "lm32-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "m32c-*-elf" against "powerpc64le-unknown-linux-gnu"
Checking "m68k-*-elf" against "powerpc64le-unknown-linux-gnu"
Checking "m68k-*-uclinux*" against "powerpc64le-unknown-linux-gnu"
Checking "mips*-*-elf*" against "powerpc64le-unknown-linux-gnu"
Checking "mmix-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "mn10300-*-elf*" against "powerpc64le-unknown-linux-gnu"
Checking "moxie-*-elf*" against "powerpc64le-unknown-linux-gnu"
Checking "msp430-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "nds32*-*-elf" against "powerpc64le-unknown-linux-gnu"
Checking "nios2-*-elf" against "powerpc64le-unknown-linux-gnu"
Checking "nvptx-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "powerpc-*-eabi*" against "powerpc64le-unknown-linux-gnu"
Checking "powerpc-*-elf" against "powerpc64le-unknown-linux-gnu"
Checking "pru-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "rx-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "tic6x-*-elf" against "powerpc64le-unknown-linux-gnu"
Checking "visium-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "xstormy16-*" against "powerpc64le-unknown-linux-gnu"
Checking "xtensa*-*-elf" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-rtems*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-vxworks*" against "powerpc64le-unknown-linux-gnu"
check_cached_effective_target profiling_available: returning 1 for unix
check_compile tool: gcc for profiling
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ profiling115344.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -pg  -lm  -o profiling115344.exe
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ profiling115344.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -pg  -lm  -o profiling115344.exe    (timeout = 300)
pid is 115383 -115383
waitres is 115383 exp6 0 0
output is  status 0
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Looking for library file ../lib/profopt.exp
Looking for library file /usr/share/dejagnu/profopt.exp
Looking for library file /usr/share/dejagnu/lib/profopt.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/profopt.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/profopt.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/profopt.exp
Looking for library file ../lib/dg.exp
Looking for library file /usr/share/dejagnu/dg.exp
Loading /usr/share/dejagnu/dg.exp
Looking for library file ../lib/gcc-dg.exp
Looking for library file /usr/share/dejagnu/gcc-dg.exp
Looking for library file /usr/share/dejagnu/lib/gcc-dg.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/gcc-dg.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/gcc-dg.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/gcc-dg.exp
Looking for library file ../lib/file-format.exp
Looking for library file /usr/share/dejagnu/file-format.exp
Looking for library file /usr/share/dejagnu/lib/file-format.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/file-format.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/file-format.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/file-format.exp
Looking for library file ../lib/target-supports-dg.exp
Looking for library file /usr/share/dejagnu/target-supports-dg.exp
Looking for library file /usr/share/dejagnu/lib/target-supports-dg.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/target-supports-dg.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/target-supports-dg.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/target-supports-dg.exp
Looking for library file ../lib/scanasm.exp
Looking for library file /usr/share/dejagnu/scanasm.exp
Looking for library file /usr/share/dejagnu/lib/scanasm.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/scanasm.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scanasm.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scanasm.exp
Looking for library file ../lib/scanrtl.exp
Looking for library file /usr/share/dejagnu/scanrtl.exp
Looking for library file /usr/share/dejagnu/lib/scanrtl.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/scanrtl.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scanrtl.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scanrtl.exp
Looking for library file ../lib/scandump.exp
Looking for library file /usr/share/dejagnu/scandump.exp
Looking for library file /usr/share/dejagnu/lib/scandump.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/scandump.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scandump.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scandump.exp
Looking for library file ../lib/scantree.exp
Looking for library file /usr/share/dejagnu/scantree.exp
Looking for library file /usr/share/dejagnu/lib/scantree.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/scantree.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scantree.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scantree.exp
Looking for library file ../lib/scanltranstree.exp
Looking for library file /usr/share/dejagnu/scanltranstree.exp
Looking for library file /usr/share/dejagnu/lib/scanltranstree.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/scanltranstree.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scanltranstree.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scanltranstree.exp
Looking for library file ../lib/scanipa.exp
Looking for library file /usr/share/dejagnu/scanipa.exp
Looking for library file /usr/share/dejagnu/lib/scanipa.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/scanipa.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scanipa.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scanipa.exp
Looking for library file ../lib/scanwpaipa.exp
Looking for library file /usr/share/dejagnu/scanwpaipa.exp
Looking for library file /usr/share/dejagnu/lib/scanwpaipa.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/scanwpaipa.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scanwpaipa.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scanwpaipa.exp
Looking for library file ../lib/scanlang.exp
Looking for library file /usr/share/dejagnu/scanlang.exp
Looking for library file /usr/share/dejagnu/lib/scanlang.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/scanlang.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scanlang.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scanlang.exp
Looking for library file ../lib/timeout-dg.exp
Looking for library file /usr/share/dejagnu/timeout-dg.exp
Looking for library file /usr/share/dejagnu/lib/timeout-dg.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/timeout-dg.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/timeout-dg.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/timeout-dg.exp
Looking for library file ../lib/torture-options.exp
Looking for library file /usr/share/dejagnu/torture-options.exp
Looking for library file /usr/share/dejagnu/lib/torture-options.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/torture-options.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/torture-options.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/torture-options.exp
Looking for library file ../lib/fortran-modules.exp
Looking for library file /usr/share/dejagnu/fortran-modules.exp
Looking for library file /usr/share/dejagnu/lib/fortran-modules.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/fortran-modules.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/fortran-modules.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/fortran-modules.exp
Checking pattern "*-*-cygwin*" with powerpc64le-unknown-linux-gnu
Checking "*-*-darwin*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-cygwin*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-mingw*" against "powerpc64le-unknown-linux-gnu"
Checking "hppa*-*-hpux*" against "powerpc64le-unknown-linux-gnu"
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ -print-file-name=libgcc_s.so   (timeout = 300)
pid is 115393 -115393
waitres is 115393 exp6 0 0
output is /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/libgcc_s.so
 status 0
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ -print-multi-directory   (timeout = 300)
pid is 115396 -115396
waitres is 115396 exp6 0 0
output is .
 status 0
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ -print-multi-lib   (timeout = 300)
pid is 115399 -115399
waitres is 115399 exp6 0 0
output is .;
 status 0
Checking "*-*-cygwin*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-mingw*" against "powerpc64le-unknown-linux-gnu"
LD_LIBRARY_PATH=:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc:/home/luoxhu/local/lib/
LD_RUN_PATH=:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc
SHLIB_PATH=:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc:/home/luoxhu/local/lib/
LD_LIBRARY_PATH_32=:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc:/home/luoxhu/local/lib/
LD_LIBRARY_PATH_64=:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc:/home/luoxhu/local/lib/
DYLD_LIBRARY_PATH=:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc
Checking "nvptx-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "amdgcn-*-*" against "powerpc64le-unknown-linux-gnu"
check_cached_effective_target lto: checking unix
check_compile tool: gcc for lto
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -flto -c -o lto115344.o lto115344.c
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -flto -c -o lto115344.o lto115344.c    (timeout = 300)
pid is 115402 -115402
waitres is 115402 exp6 0 0
output is  status 0
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
check_cached_effective_target lto: returning 1 for unix
check_compile tool: gcc for linker_plugin
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ linker_plugin115344.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -flto -fuse-linker-plugin  -lm  -o linker_plugin115344.exe
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ linker_plugin115344.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -flto -fuse-linker-plugin  -lm  -o linker_plugin115344.exe    (timeout = 300)
pid is 115409 -115409
waitres is 115409 exp6 0 0
output is  status 0
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
check_cached_effective_target llvm_binutils: checking unix
Checking "amdgcn*-*-*" against "powerpc64le-unknown-linux-gnu"
check_cached_effective_target offload_gcn: checking unix
check_compile tool: gcc for offload_gcn
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ offload_gcn115344.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -foffload=amdgcn-unknown-amdhsa -S -o offload_gcn115344.s
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ offload_gcn115344.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -foffload=amdgcn-unknown-amdhsa -S -o offload_gcn115344.s    (timeout = 300)
pid is 115427 -115427
waitres is 115427 exp6 0 1
close result is 115427 exp6 0 1
output is xgcc: fatal error: GCC is not configured to support amdgcn-unknown-amdhsa as offload target
compilation terminated.
 status 1
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
compiler exited with status 1
output is:
xgcc: fatal error: GCC is not configured to support amdgcn-unknown-amdhsa as offload target
compilation terminated.

check_cached_effective_target offload_gcn: returning 0 for unix
check_cached_effective_target llvm_binutils: returning 0 for unix
Testing gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c, 
Checking "nvptx-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "amdgcn-*-*" against "powerpc64le-unknown-linux-gnu"
check_cached_effective_target lto: returning 1 for unix
is-effective-target: lto 1
Profiling argument is <-fprofile-generate>
Checking "powerpc-ibm-aix*" against "powerpc64le-unknown-linux-gnu"
check_cached_effective_target tls_runtime: returning 1 for unix
Checking "*-*-solaris2*" against "powerpc64le-unknown-linux-gnu"
Checking "mips*-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-mingw*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-cygwin*" against "powerpc64le-unknown-linux-gnu"
check_cached_effective_target uclibc: returning 0 for unix
check_cached_effective_target profiling_available: returning 1 for unix
check_compile tool: gcc for profiling
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ profiling115344.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -pg  -lm  -o profiling115344.exe
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ profiling115344.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -pg  -lm  -o profiling115344.exe    (timeout = 300)
pid is 115430 -115430
waitres is 115430 exp6 0 0
output is  status 0
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking "powerpc-ibm-aix*" against "powerpc64le-unknown-linux-gnu"
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never   /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c  -O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate -fprofile-generate -D_PROFILE_GENERATE  -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indir-call-topn-1.x01
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never   /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c  -O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate -fprofile-generate -D_PROFILE_GENERATE  -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indir-call-topn-1.x01    (timeout = 300)
pid is 115440 -115440
waitres is 115440 exp6 0 0
output is  status 0
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
check_cached_effective_target exceptions_enabled: checking unix
Checking "amdgcn*-*-*" against "powerpc64le-unknown-linux-gnu"
check_cached_effective_target exceptions_enabled: checking unix
check_compile tool: gcc for exceptions_enabled
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ exceptions_enabled115344.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -S -o exceptions_enabled115344.s
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ exceptions_enabled115344.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -S -o exceptions_enabled115344.s    (timeout = 300)
pid is 115462 -115462
waitres is 115462 exp6 0 1
close result is 115462 exp6 0 1
output is exceptions_enabled115344.c: In function 'foo':
exceptions_enabled115344.c:4:7: error: 'throw' undeclared (first use in this function)
exceptions_enabled115344.c:4:7: note: each undeclared identifier is reported only once for each function it appears in
exceptions_enabled115344.c:4:12: error: expected ';' before numeric constant
 status 1
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
compiler exited with status 1
output is:
exceptions_enabled115344.c: In function 'foo':
exceptions_enabled115344.c:4:7: error: 'throw' undeclared (first use in this function)
exceptions_enabled115344.c:4:7: note: each undeclared identifier is reported only once for each function it appears in
exceptions_enabled115344.c:4:12: error: expected ';' before numeric constant

check_cached_effective_target exceptions_enabled: returning 0 for unix
check_cached_effective_target exceptions_enabled: returning 0 for unix
Checking powerpc64le-unknown-linux-gnu against powerpc64le-unknown-linux-gnu
loading to unix
Setting LD_LIBRARY_PATH to :/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc::/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc:/home/luoxhu/local/lib/
spawning command /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indir-call-topn-1.x01 
exp9 file6
Executed /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indir-call-topn-1.x01, status 0
done:0

doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never   /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c  -O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate -fprofile-use -D_PROFILE_USE  -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indir-call-topn-1.x02
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never   /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c  -O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate -fprofile-use -D_PROFILE_USE  -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indir-call-topn-1.x02    (timeout = 300)
pid is 115469 -115469
waitres is 115469 exp6 0 0
output is  status 0
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
check_cached_effective_target exceptions_enabled: returning 0 for unix
Checking powerpc64le-unknown-linux-gnu against powerpc64le-unknown-linux-gnu
loading to unix
Setting LD_LIBRARY_PATH to :/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc::/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc:/home/luoxhu/local/lib/
spawning command /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indir-call-topn-1.x02 
exp9 file6
Executed /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indir-call-topn-1.x02, status 0
done:0

remove-build-file `{crossmodule-indir-call-topn-1.{c,exe},crossmodule-indir-call-topn-1a.{c,exe}}.{ltrans[0-9]*.,}[0-9][0-9][0-9]{l,i,r,t}.*'
remove-build-file `crossmodule-indir-call-topn-1.c.068i.profile_estimate crossmodule-indir-call-topn-1a.c.068i.profile_estimate'
Testing gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c, 
Checking "powerpc-ibm-aix*" against "powerpc64le-unknown-linux-gnu"
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never    -fprofile-generate -D_PROFILE_GENERATE  -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indir-call-topn-1a.x01
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never    -fprofile-generate -D_PROFILE_GENERATE  -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indir-call-topn-1a.x01    (timeout = 300)
pid is 115493 -115493
waitres is 115493 exp6 0 0
output is  status 0
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
check_cached_effective_target exceptions_enabled: returning 0 for unix
Checking powerpc64le-unknown-linux-gnu against powerpc64le-unknown-linux-gnu
loading to unix
Setting LD_LIBRARY_PATH to :/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc::/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc:/home/luoxhu/local/lib/
spawning command /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indir-call-topn-1a.x01 
exp9 file6
Executed /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indir-call-topn-1a.x01, status 0
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never    -fprofile-use -D_PROFILE_USE  -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indir-call-topn-1a.x02
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never    -fprofile-use -D_PROFILE_USE  -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indir-call-topn-1a.x02    (timeout = 300)
pid is 115506 -115506
waitres is 115506 exp6 0 0
output is  status 0
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
check_cached_effective_target exceptions_enabled: returning 0 for unix
Checking powerpc64le-unknown-linux-gnu against powerpc64le-unknown-linux-gnu
loading to unix
Setting LD_LIBRARY_PATH to :/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc::/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc:/home/luoxhu/local/lib/
spawning command /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indir-call-topn-1a.x02 
exp9 file6
Executed /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indir-call-topn-1a.x02, status 0
Testing gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c, 
Checking "nvptx-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "amdgcn-*-*" against "powerpc64le-unknown-linux-gnu"
check_cached_effective_target lto: returning 1 for unix
is-effective-target: lto 1
Profiling argument is <-fprofile-generate>
Checking "powerpc-ibm-aix*" against "powerpc64le-unknown-linux-gnu"
check_cached_effective_target tls_runtime: returning 1 for unix
Checking "*-*-solaris2*" against "powerpc64le-unknown-linux-gnu"
Checking "mips*-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-mingw*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-cygwin*" against "powerpc64le-unknown-linux-gnu"
check_cached_effective_target uclibc: returning 0 for unix
check_cached_effective_target profiling_available: returning 1 for unix
check_compile tool: gcc for profiling
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ profiling115344.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -pg  -lm  -o profiling115344.exe
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ profiling115344.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -pg  -lm  -o profiling115344.exe    (timeout = 300)
pid is 115518 -115518
waitres is 115518 exp6 0 0
output is  status 0
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking "powerpc-ibm-aix*" against "powerpc64le-unknown-linux-gnu"
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never   /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c  -O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate -fprofile-generate -D_PROFILE_GENERATE  -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indir-call-topn-2.x01
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never   /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c  -O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate -fprofile-generate -D_PROFILE_GENERATE  -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indir-call-topn-2.x01    (timeout = 300)
pid is 115528 -115528
waitres is 115528 exp6 0 0
output is  status 0
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
check_cached_effective_target exceptions_enabled: returning 0 for unix
Checking powerpc64le-unknown-linux-gnu against powerpc64le-unknown-linux-gnu
loading to unix
Setting LD_LIBRARY_PATH to :/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc::/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc:/home/luoxhu/local/lib/
spawning command /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indir-call-topn-2.x01 
exp9 file6
Executed /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indir-call-topn-2.x01, status 0
done:0

doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never   /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c  -O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate -fprofile-use -D_PROFILE_USE  -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indir-call-topn-2.x02
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never   /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indir-call-topn-1a.c  -O2 -flto -DDOJOB=1 -fdump-ipa-profile_estimate -fprofile-use -D_PROFILE_USE  -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indir-call-topn-2.x02    (timeout = 300)
pid is 115658 -115658
waitres is 115658 exp6 0 0
output is  status 0
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
check_cached_effective_target exceptions_enabled: returning 0 for unix
Checking powerpc64le-unknown-linux-gnu against powerpc64le-unknown-linux-gnu
loading to unix
Setting LD_LIBRARY_PATH to :/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc::/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc:/home/luoxhu/local/lib/
spawning command /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indir-call-topn-2.x02 
exp9 file6
Executed /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indir-call-topn-2.x02, status 0
done:0

remove-build-file `{crossmodule-indir-call-topn-2.{c,exe},crossmodule-indir-call-topn-1a.{c,exe}}.{ltrans[0-9]*.,}[0-9][0-9][0-9]{l,i,r,t}.*'
remove-build-file `crossmodule-indir-call-topn-2.c.068i.profile_estimate crossmodule-indir-call-topn-1a.c.068i.profile_estimate'
Testing gcc.dg/tree-prof/crossmodule-indircall-1.c, 
Checking "nvptx-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "amdgcn-*-*" against "powerpc64le-unknown-linux-gnu"
check_cached_effective_target lto: returning 1 for unix
is-effective-target: lto 1
Checking "powerpc-ibm-aix*" against "powerpc64le-unknown-linux-gnu"
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indircall-1.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never   /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indircall-1a.c  -O3 -flto -DDOJOB=1 -fprofile-generate -D_PROFILE_GENERATE  -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indircall-1.x01
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indircall-1.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never   /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indircall-1a.c  -O3 -flto -DDOJOB=1 -fprofile-generate -D_PROFILE_GENERATE  -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indircall-1.x01    (timeout = 300)
pid is 115682 -115682
waitres is 115682 exp6 0 0
output is  status 0
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
check_cached_effective_target exceptions_enabled: returning 0 for unix
Checking powerpc64le-unknown-linux-gnu against powerpc64le-unknown-linux-gnu
loading to unix
Setting LD_LIBRARY_PATH to :/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc::/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc:/home/luoxhu/local/lib/
spawning command /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indircall-1.x01 
exp9 file6
Executed /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indircall-1.x01, status 0
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indircall-1.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never   /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indircall-1a.c  -O3 -flto -DDOJOB=1 -fprofile-use -D_PROFILE_USE  -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indircall-1.x02
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indircall-1.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never   /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indircall-1a.c  -O3 -flto -DDOJOB=1 -fprofile-use -D_PROFILE_USE  -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indircall-1.x02    (timeout = 300)
pid is 115707 -115707
waitres is 115707 exp6 0 0
output is  status 0
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
check_cached_effective_target exceptions_enabled: returning 0 for unix
Checking powerpc64le-unknown-linux-gnu against powerpc64le-unknown-linux-gnu
loading to unix
Setting LD_LIBRARY_PATH to :/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc::/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc:/home/luoxhu/local/lib/
spawning command /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indircall-1.x02 
exp9 file6
Executed /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indircall-1.x02, status 0
Testing gcc.dg/tree-prof/crossmodule-indircall-1a.c, 
Checking "powerpc-ibm-aix*" against "powerpc64le-unknown-linux-gnu"
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indircall-1a.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never    -fprofile-generate -D_PROFILE_GENERATE  -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indircall-1a.x01
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indircall-1a.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never    -fprofile-generate -D_PROFILE_GENERATE  -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indircall-1a.x01    (timeout = 300)
pid is 115731 -115731
waitres is 115731 exp6 0 0
output is  status 0
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
check_cached_effective_target exceptions_enabled: returning 0 for unix
Checking powerpc64le-unknown-linux-gnu against powerpc64le-unknown-linux-gnu
loading to unix
Setting LD_LIBRARY_PATH to :/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc::/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc:/home/luoxhu/local/lib/
spawning command /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indircall-1a.x01 
exp9 file6
Executed /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indircall-1a.x01, status 0
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indircall-1a.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never    -fprofile-use -D_PROFILE_USE  -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indircall-1a.x02
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-prof/crossmodule-indircall-1a.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never    -fprofile-use -D_PROFILE_USE  -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indircall-1a.x02    (timeout = 300)
pid is 115744 -115744
waitres is 115744 exp6 0 0
output is  status 0
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
check_cached_effective_target exceptions_enabled: returning 0 for unix
Checking powerpc64le-unknown-linux-gnu against powerpc64le-unknown-linux-gnu
loading to unix
Setting LD_LIBRARY_PATH to :/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc::/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc:/home/luoxhu/local/lib/
spawning command /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indircall-1a.x02 
exp9 file6
Executed /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gcc/crossmodule-indircall-1a.x02, status 0
Profiling argument is <-fauto-profile>
Checking "i?86-*-linux*" against "powerpc64le-unknown-linux-gnu"
Checking "x86_64-*-linux*" against "powerpc64le-unknown-linux-gnu"
autofdo only supported on linux
Profiling argument is <-fauto-profile>
Checking "i?86-*-linux*" against "powerpc64le-unknown-linux-gnu"
Checking "x86_64-*-linux*" against "powerpc64le-unknown-linux-gnu"
autofdo only supported on linux
Profiling argument is <-fauto-profile>
Checking "i?86-*-linux*" against "powerpc64le-unknown-linux-gnu"
Checking "x86_64-*-linux*" against "powerpc64le-unknown-linux-gnu"
autofdo only supported on linux
Profiling argument is <-fauto-profile>
Checking "i?86-*-linux*" against "powerpc64le-unknown-linux-gnu"
Checking "x86_64-*-linux*" against "powerpc64le-unknown-linux-gnu"
autofdo only supported on linux
Profiling argument is <-fauto-profile>
Checking "i?86-*-linux*" against "powerpc64le-unknown-linux-gnu"
Checking "x86_64-*-linux*" against "powerpc64le-unknown-linux-gnu"
autofdo only supported on linux
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tree-ssa/tree-ssa.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/tsan/tsan.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/ubsan/ubsan.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/vect/costmodel/i386/i386-costmodel-vect.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/vect/costmodel/ppc/ppc-costmodel-vect.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/vect/costmodel/x86_64/x86_64-costmodel-vect.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/vect/vect.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/vmx/vmx.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/vxworks/vxworks.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg/weak/weak.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.dg-selftests/dg-final.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.misc-tests/acker1.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.misc-tests/arm-isr.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.misc-tests/bprob.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.misc-tests/dectest.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.misc-tests/dhry.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.misc-tests/gcov.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.misc-tests/godump.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.misc-tests/help.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.misc-tests/i386-prefetch.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.misc-tests/linkage.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.misc-tests/matrix1.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.misc-tests/mg-2.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.misc-tests/mg.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.misc-tests/options.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.misc-tests/output.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.misc-tests/sieve.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.misc-tests/sort2.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.src/maintainers.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/aarch64/aapcs64/aapcs64.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/aarch64/aarch64.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/aarch64/acle/acle.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/aarch64/advsimd-intrinsics/advsimd-intrinsics.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/aarch64/fp16/fp16.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/aarch64/simd/simd.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/aarch64/sve/aarch64-sve.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/aarch64/sve/acle/aarch64-sve-acle-asm.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/aarch64/sve/acle/aarch64-sve-acle.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/aarch64/sve/pcs/aarch64-sve-pcs.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/aarch64/sve2/aarch64-sve2.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/aarch64/sve2/acle/aarch64-sve2-acle-asm.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/aarch64/sve2/acle/aarch64-sve2-acle.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/aarch64/torture/aarch64-torture.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/alpha/alpha.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/arc/arc.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/arm/aapcs/aapcs.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/arm/acle/acle.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/arm/arm.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/arm/cmse/cmse.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/arm/lto/lto.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/arm/multilib.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/arm/pure-code/pure-code.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/arm/simd/simd.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/avr/avr.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/avr/mmcu/avr-mmcu.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/avr/torture/avr-torture.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/bfin/bfin.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/bfin/builtins/bfin-builtins.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/bpf/bpf.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/cris/cris.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/cris/torture/cris-torture.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/csky/csky.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/epiphany/epiphany.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/frv/frv.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/h8300/h8300.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/hppa/hppa.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/i386/i386.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/i386/iamcu/abi-iamcu.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/i386/math-torture/math-torture.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/i386/stackalign/stackalign.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/ia64/ia64.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/m68k/m68k.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/microblaze/microblaze.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/mips/inter/mips16-inter.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/mips/mips-nonpic/mips-nonpic.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/mips/mips.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/msp430/msp430.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/nds32/nds32.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/nios2/nios2.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/nvptx/nvptx.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/or1k/or1k.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/powerpc/bfp/bfp.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/powerpc/dfp/dfp.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/powerpc/powerpc.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/powerpc/ppc-fortran/ppc-fortran.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/powerpc/vsu/vsu.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/pru/pru.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/riscv/riscv.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/rl78/rl78.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/rx/rx.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/s390/s390.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/sh/sh.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/sh/torture/sh-torture.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/sparc/sparc.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/tic6x/builtins/c6x-builtins.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/tic6x/tic6x.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/vax/vax.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/visium/visium.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/x86_64/abi/abi-x86_64.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/x86_64/abi/avx/abi-avx.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/x86_64/abi/avx512f/abi-avx512f.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/x86_64/abi/callabi/callabi.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/ms-sysv.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/xstormy16/xstormy16.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.target/xtensa/xtensa.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gcc.test-framework/test-framework.exp in tree-prof.exp

		=== gcc Summary ===

# of expected passes		22
# of unsupported tests		5
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc -v    (timeout = 300)
pid is 115756 -115756
waitres is 115756 exp6 0 0
output is Using built-in specs.
COLLECT_GCC=/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc
COLLECT_LTO_WRAPPER=/home/luoxhu/local/gcc_10/libexec/gcc/powerpc64le-unknown-linux-gnu/10.0.0/lto-wrapper
Target: powerpc64le-unknown-linux-gnu
Configured with: ../gcc-trunk/configure --enable-languages=c,c++,fortran --prefix=/home/luoxhu/local/gcc_10/ --disable-libsanitizer --disable-bootstrap : (reconfigured) ../gcc-trunk/configure --prefix=/home/luoxhu/local/gcc_10/ --disable-libsanitizer --disable-bootstrap --enable-languages=c,c++,fortran,lto --no-create --no-recursion
Thread model: posix
Supported LTO compression algorithms: zlib
gcc version 10.0.0 20200114 (experimental) (GCC) 
 status 0
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc  version 10.0.0 20200114 (experimental) (GCC) 

runtest completed at Tue Jan 14 08:22:42 2020
make[2]: Leaving directory '/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc'
rm -rf testsuite/g++-parallel
make[2]: Entering directory '/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc'
(rootme=`${PWDCMD-pwd}`; export rootme; \
srcdir=`cd ../../gcc-trunk/gcc; ${PWDCMD-pwd}` ; export srcdir ; \
if [ -n "" ] \
   && [ -n "$GCC_RUNTEST_PARALLELIZE_DIR" ] \
   && [ -f testsuite/g++-parallel/finished ]; then \
  rm -rf testsuite/g++; \
else \
  cd testsuite/g++; \
  rm -f tmp-site.exp; \
  sed '/set tmpdir/ s|testsuite$|testsuite/g++|' \
	< ../../site.exp > tmp-site.exp; \
  /bin/sh ${srcdir}/../move-if-change tmp-site.exp site.exp; \
  EXPECT=`if [ -f ${rootme}/../expect/expect ] ; then echo ${rootme}/../expect/expect ; else echo expect ; fi` ; export EXPECT ; \
  if [ -f ${rootme}/../expect/expect ] ; then  \
    TCL_LIBRARY=`cd .. ; cd ${srcdir}/../tcl/library ; ${PWDCMD-pwd}` ; \
    export TCL_LIBRARY ; \
  fi ; \
  `if [ -f ${srcdir}/../dejagnu/runtest ] ; then echo ${srcdir}/../dejagnu/runtest ; else echo runtest; fi` --tool g++ tree-prof.exp=crossmodule-* -v -v; \
  if [ -n "$GCC_RUNTEST_PARALLELIZE_DIR" ] ; then \
    touch ${rootme}/testsuite/g++-parallel/finished; \
  fi ; \
fi )
Expect binary is expect
Looking for /usr/share/dejagnu/runtest.exp.
Using /usr/share/dejagnu/runtest.exp as main test driver
Verbose level is 2
Login name is luoxhu
Looking for ~/.dejagnurc
Looking for /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/site.exp
Found /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/site.exp
Using test sources in /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite
Using test binaries in /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++
Tool root directory is /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite
Using /usr/share/dejagnu to find libraries
Looking for /usr/share/dejagnu/site.exp
Found /usr/share/dejagnu/site.exp
Looking for library file ../lib/utils.exp
Looking for library file /usr/share/dejagnu/utils.exp
Loading /usr/share/dejagnu/utils.exp
Looking for library file ../lib/framework.exp
Looking for library file /usr/share/dejagnu/framework.exp
Loading /usr/share/dejagnu/framework.exp
Looking for library file ../lib/debugger.exp
Looking for library file /usr/share/dejagnu/debugger.exp
Loading /usr/share/dejagnu/debugger.exp
Looking for library file ../lib/remote.exp
Looking for library file /usr/share/dejagnu/remote.exp
Loading /usr/share/dejagnu/remote.exp
Looking for library file ../lib/telnet.exp
Looking for library file /usr/share/dejagnu/telnet.exp
Loading /usr/share/dejagnu/telnet.exp
Looking for library file ../lib/rlogin.exp
Looking for library file /usr/share/dejagnu/rlogin.exp
Loading /usr/share/dejagnu/rlogin.exp
Looking for library file ../lib/kermit.exp
Looking for library file /usr/share/dejagnu/kermit.exp
Loading /usr/share/dejagnu/kermit.exp
Looking for library file ../lib/tip.exp
Looking for library file /usr/share/dejagnu/tip.exp
Loading /usr/share/dejagnu/tip.exp
Looking for library file ../lib/rsh.exp
Looking for library file /usr/share/dejagnu/rsh.exp
Loading /usr/share/dejagnu/rsh.exp
Looking for library file ../lib/ftp.exp
Looking for library file /usr/share/dejagnu/ftp.exp
Loading /usr/share/dejagnu/ftp.exp
Looking for library file ../lib/target.exp
Looking for library file /usr/share/dejagnu/target.exp
Loading /usr/share/dejagnu/target.exp
Looking for library file ../lib/targetdb.exp
Looking for library file /usr/share/dejagnu/targetdb.exp
Loading /usr/share/dejagnu/targetdb.exp
Looking for library file ../lib/libgloss.exp
Looking for library file /usr/share/dejagnu/libgloss.exp
Loading /usr/share/dejagnu/libgloss.exp
Loading library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/g++.exp
Looking for library file ../lib/prune.exp
Looking for library file /usr/share/dejagnu/prune.exp
Looking for library file /usr/share/dejagnu/lib/prune.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/prune.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/prune.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/prune.exp
Looking for library file ../lib/multiline.exp
Looking for library file /usr/share/dejagnu/multiline.exp
Looking for library file /usr/share/dejagnu/lib/multiline.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/multiline.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/multiline.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/multiline.exp
Looking for library file ../lib/gcc-defs.exp
Looking for library file /usr/share/dejagnu/gcc-defs.exp
Looking for library file /usr/share/dejagnu/lib/gcc-defs.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/gcc-defs.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/gcc-defs.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/gcc-defs.exp
Looking for library file ../lib/target-libpath.exp
Looking for library file /usr/share/dejagnu/target-libpath.exp
Looking for library file /usr/share/dejagnu/lib/target-libpath.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/target-libpath.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/target-libpath.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/target-libpath.exp
Looking for library file ../lib/wrapper.exp
Looking for library file /usr/share/dejagnu/wrapper.exp
Looking for library file /usr/share/dejagnu/lib/wrapper.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/wrapper.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/wrapper.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/wrapper.exp
Looking for library file ../lib/target-utils.exp
Looking for library file /usr/share/dejagnu/target-utils.exp
Looking for library file /usr/share/dejagnu/lib/target-utils.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/target-utils.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/target-utils.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/target-utils.exp
Looking for library file ../lib/target-supports.exp
Looking for library file /usr/share/dejagnu/target-supports.exp
Looking for library file /usr/share/dejagnu/lib/target-supports.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/target-supports.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/target-supports.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/target-supports.exp
Looking for library file ../lib/timeout.exp
Looking for library file /usr/share/dejagnu/timeout.exp
Looking for library file /usr/share/dejagnu/lib/timeout.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/timeout.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/timeout.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/timeout.exp
Testing g++
Running only tests tree-prof.exp=crossmodule-*
Opening log files in .
Test Run By luoxhu on Tue Jan 14 08:22:42 2020
Checking powerpc64le-unknown-linux-gnu against powerpc64le-unknown-linux-gnu
Native configuration is powerpc64le-unknown-linux-gnu

		=== g++ tests ===

setting trap for SIGTERM to terminated
setting trap for SIGINT to interrupted by user
setting trap for SIGQUIT to interrupted by user
dirlist is /usr/share/dejagnu/baseboards
Looking for standard board description file for build /usr/share/dejagnu/baseboards/standard.exp
Looking for board description file for build /usr/share/dejagnu/baseboards/genoa.exp
pushing config for build, name is genoa
dirlist is /usr/share/dejagnu/baseboards
Looking for standard board description file for host /usr/share/dejagnu/baseboards/standard.exp
Looking for board description file for host /usr/share/dejagnu/baseboards/genoa.exp
pushing config for host, name is genoa
Schedule of variations:
    unix

target is unix
Running target unix
Looking for tool-and-target-specific interface file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/config/base-config.exp
Looking for tool-and-target-specific interface file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/../config/base-config.exp
Looking for tool-and-target-specific interface file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/../../config/base-config.exp
Looking for tool-and-target-specific interface file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/../../../config/base-config.exp
dirlist is /usr/share/dejagnu/baseboards/genoa /usr/share/dejagnu/baseboards
Looking for standard board description file for target /usr/share/dejagnu/baseboards/genoa/standard.exp
Looking for standard board description file for target /usr/share/dejagnu/baseboards/standard.exp
Looking for board description file for target /usr/share/dejagnu/baseboards/genoa/unix.exp
Looking for board description file for target /usr/share/dejagnu/baseboards/unix.exp
Using /usr/share/dejagnu/baseboards/unix.exp as board description file for target.
Looking for generic interface file for target /usr/share/dejagnu/config/unix.exp
Using /usr/share/dejagnu/config/unix.exp as generic interface file for target.
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/./xgcc
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/../xgcc
Checking "*-*-cygwin*" against "powerpc64le-unknown-linux-gnu"
Checking "alpha*-*" against "powerpc64le-unknown-linux-gnu"
Checking "sparc64-*-linux-gnu" against "powerpc64le-unknown-linux-gnu"
Looking for tool-and-target-specific interface file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/config/unix.exp
Looking for tool-and-target-specific interface file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/config/gnu.exp
Looking for tool-and-target-specific interface file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/config/default.exp
Using /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/config/default.exp as tool-and-target-specific interface file.
Looking for library file ../lib/standard.exp
Looking for library file /usr/share/dejagnu/standard.exp
Loading /usr/share/dejagnu/standard.exp
pushing config for target, name is unix
Top level testsuite dirs are /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.old-deja /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.target
trying to glob /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/asan/asan.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/bprob/bprob.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/charset/charset.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/compat/compat.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/compat/struct-layout-1.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/debug/debug.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/debug/dwarf2/dwarf2.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/dfp/dfp.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/dg.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/gcov/gcov.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/goacc-gomp/goacc-gomp.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/goacc/goacc.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/gomp/gomp.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/graphite/graphite.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/guality/guality.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/lto/lto.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/pch/pch.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/plugin/plugin.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/simulate-thread/simulate-thread.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/special/ecos.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/tls/tls.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/tm/tm.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/torture/dg-torture.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/torture/stackalign/stackalign.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/tree-prof/tree-prof.exp in tree-prof.exp
Running /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/tree-prof/tree-prof.exp ...
Checking pattern "*-*-cygwin*" with powerpc64le-unknown-linux-gnu
Seeing if /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/xg++ exists.
Didn't find file /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/xg++, returning c++
Seeing if /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ exists.
Found file, returning /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../
Checking pattern "*-dos-*" with powerpc64le-unknown-linux-gnu
compiler is /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/./powerpc64le-unknown-linux-gnu
GCC path is .
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/././libstdc++-v3
Checking "*-*-darwin*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-cygwin*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-mingw*" against "powerpc64le-unknown-linux-gnu"
Checking "hppa*-*-hpux*" against "powerpc64le-unknown-linux-gnu"
shared lib extension: so
Checking "*-*-darwin*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-cygwin*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-mingw*" against "powerpc64le-unknown-linux-gnu"
Checking "hppa*-*-hpux*" against "powerpc64le-unknown-linux-gnu"
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../ -print-file-name=libgcc_s.so   (timeout = 300)
pid is 115884 -115884
waitres is 115884 exp6 0 0
output is /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../libgcc_s.so
 status 0
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../ -print-multi-directory   (timeout = 300)
pid is 115888 -115888
waitres is 115888 exp6 0 0
output is .
 status 0
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../ -print-multi-lib   (timeout = 300)
pid is 115891 -115891
waitres is 115891 exp6 0 0
output is .;
 status 0
Checking "*-*-cygwin*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-mingw*" against "powerpc64le-unknown-linux-gnu"
LD_LIBRARY_PATH=.:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../..:/home/luoxhu/local/lib/
LD_RUN_PATH=.:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../..
SHLIB_PATH=.:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../..
LD_LIBRARY_PATH_32=.:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../..:/home/luoxhu/local/lib/
LD_LIBRARY_PATH_64=.:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../..:/home/luoxhu/local/lib/
DYLD_LIBRARY_PATH=.:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../..
Checking "*-*-darwin*" against "powerpc64le-unknown-linux-gnu"
ALWAYS_CXXFLAGS set to {additional_flags=-fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never } {additional_flags=-nostdinc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/powerpc64le-unknown-linux-gnu -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/libsupc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/include/backward -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/testsuite/util} {ldflags= -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/ -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs} additional_flags=-fmessage-length=0
Profiling argument is <-fprofile-generate>
Checking "powerpc-ibm-aix*" against "powerpc64le-unknown-linux-gnu"
check_cached_effective_target tls_runtime: checking unix
check_compile tool: g++ for tls_runtime
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/././libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/../../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/../../../libio/_G_config.h
/home/luoxhu/workspace/../../../../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/././libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/../libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/../../libio/iostream.list
/home/luoxhu/workspace/gcc-git/../../../libio/iostream.list
/home/luoxhu/workspace/../../../../libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/./libio/Makefile.in
/home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/../libio/Makefile.in
/home/luoxhu/workspace/gcc-git/gcc-trunk/../../libio/Makefile.in
/home/luoxhu/workspace/gcc-git/../../../libio/Makefile.in
/home/luoxhu/workspace/../../../../libio/Makefile.in
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../ tls_runtime115858.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -nostdinc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/powerpc64le-unknown-linux-gnu -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/libsupc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/include/backward -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/testsuite/util -fmessage-length=0     -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/ -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs -lm  -o tls_runtime115858.exe
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../ tls_runtime115858.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -nostdinc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/powerpc64le-unknown-linux-gnu -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/libsupc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/include/backward -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/testsuite/util -fmessage-length=0     -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/ -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs -lm  -o tls_runtime115858.exe    (timeout = 300)
pid is 115894 -115894
waitres is 115894 exp6 0 0
output is  status 0
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
loading to unix
Setting LD_LIBRARY_PATH to .:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../..:.:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../..:/home/luoxhu/local/lib/
spawning command ./tls_runtime115858.exe 
exp9 file6
Executed ./tls_runtime115858.exe, status 0
check_runtime_nocache tls_runtime: status is <pass>
check_cached_effective_target tls_runtime: returning 1 for unix
Checking "*-*-solaris2*" against "powerpc64le-unknown-linux-gnu"
Checking "mips*-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-mingw*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-cygwin*" against "powerpc64le-unknown-linux-gnu"
check_cached_effective_target uclibc: checking unix
check_compile tool: g++ for uclibc
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/././libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/../../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/../../../libio/_G_config.h
/home/luoxhu/workspace/../../../../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/././libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/../libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/../../libio/iostream.list
/home/luoxhu/workspace/gcc-git/../../../libio/iostream.list
/home/luoxhu/workspace/../../../../libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/./libio/Makefile.in
/home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/../libio/Makefile.in
/home/luoxhu/workspace/gcc-git/gcc-trunk/../../libio/Makefile.in
/home/luoxhu/workspace/gcc-git/../../../libio/Makefile.in
/home/luoxhu/workspace/../../../../libio/Makefile.in
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -nostdinc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/powerpc64le-unknown-linux-gnu -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/libsupc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/include/backward -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/testsuite/util -fmessage-length=0  -c -o uclibc115858.o uclibc115858.c
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -nostdinc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/powerpc64le-unknown-linux-gnu -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/libsupc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/include/backward -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/testsuite/util -fmessage-length=0  -c -o uclibc115858.o uclibc115858.c    (timeout = 300)
pid is 115906 -115906
waitres is 115906 exp6 0 1
close result is 115906 exp6 0 1
output is uclibc115858.c:4:3: error: #error !__UCLIBC__
 status 1
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
compiler exited with status 1
output is:
uclibc115858.c:4:3: error: #error !__UCLIBC__

check_cached_effective_target uclibc: returning 0 for unix
check_cached_effective_target profiling_available: checking unix
Checking "aarch64*-*-elf" against "powerpc64le-unknown-linux-gnu"
Checking "am3*-*-linux*" against "powerpc64le-unknown-linux-gnu"
Checking "amdgcn-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "arm*-*-eabi*" against "powerpc64le-unknown-linux-gnu"
Checking "arm*-*-elf" against "powerpc64le-unknown-linux-gnu"
Checking "arm*-*-symbianelf*" against "powerpc64le-unknown-linux-gnu"
Checking "avr-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "bfin-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "cris-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "crisv32-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "csky-*-elf" against "powerpc64le-unknown-linux-gnu"
Checking "fido-*-elf" against "powerpc64le-unknown-linux-gnu"
Checking "h8300-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "lm32-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "m32c-*-elf" against "powerpc64le-unknown-linux-gnu"
Checking "m68k-*-elf" against "powerpc64le-unknown-linux-gnu"
Checking "m68k-*-uclinux*" against "powerpc64le-unknown-linux-gnu"
Checking "mips*-*-elf*" against "powerpc64le-unknown-linux-gnu"
Checking "mmix-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "mn10300-*-elf*" against "powerpc64le-unknown-linux-gnu"
Checking "moxie-*-elf*" against "powerpc64le-unknown-linux-gnu"
Checking "msp430-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "nds32*-*-elf" against "powerpc64le-unknown-linux-gnu"
Checking "nios2-*-elf" against "powerpc64le-unknown-linux-gnu"
Checking "nvptx-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "powerpc-*-eabi*" against "powerpc64le-unknown-linux-gnu"
Checking "powerpc-*-elf" against "powerpc64le-unknown-linux-gnu"
Checking "pru-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "rx-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "tic6x-*-elf" against "powerpc64le-unknown-linux-gnu"
Checking "visium-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "xstormy16-*" against "powerpc64le-unknown-linux-gnu"
Checking "xtensa*-*-elf" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-rtems*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-vxworks*" against "powerpc64le-unknown-linux-gnu"
check_cached_effective_target profiling_available: returning 1 for unix
check_compile tool: g++ for profiling
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/././libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/../../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/../../../libio/_G_config.h
/home/luoxhu/workspace/../../../../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/././libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/../libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/../../libio/iostream.list
/home/luoxhu/workspace/gcc-git/../../../libio/iostream.list
/home/luoxhu/workspace/../../../../libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/./libio/Makefile.in
/home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/../libio/Makefile.in
/home/luoxhu/workspace/gcc-git/gcc-trunk/../../libio/Makefile.in
/home/luoxhu/workspace/gcc-git/../../../libio/Makefile.in
/home/luoxhu/workspace/../../../../libio/Makefile.in
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../ profiling115858.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -nostdinc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/powerpc64le-unknown-linux-gnu -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/libsupc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/include/backward -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/testsuite/util -fmessage-length=0 -pg    -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/ -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs -lm  -o profiling115858.exe
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../ profiling115858.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -nostdinc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/powerpc64le-unknown-linux-gnu -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/libsupc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/include/backward -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/testsuite/util -fmessage-length=0 -pg    -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/ -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs -lm  -o profiling115858.exe    (timeout = 300)
pid is 115910 -115910
waitres is 115910 exp6 0 0
output is  status 0
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Looking for library file ../lib/profopt.exp
Looking for library file /usr/share/dejagnu/profopt.exp
Looking for library file /usr/share/dejagnu/lib/profopt.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/profopt.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/profopt.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/profopt.exp
Looking for library file ../lib/dg.exp
Looking for library file /usr/share/dejagnu/dg.exp
Loading /usr/share/dejagnu/dg.exp
Looking for library file ../lib/gcc-dg.exp
Looking for library file /usr/share/dejagnu/gcc-dg.exp
Looking for library file /usr/share/dejagnu/lib/gcc-dg.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/gcc-dg.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/gcc-dg.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/gcc-dg.exp
Looking for library file ../lib/file-format.exp
Looking for library file /usr/share/dejagnu/file-format.exp
Looking for library file /usr/share/dejagnu/lib/file-format.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/file-format.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/file-format.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/file-format.exp
Looking for library file ../lib/target-supports-dg.exp
Looking for library file /usr/share/dejagnu/target-supports-dg.exp
Looking for library file /usr/share/dejagnu/lib/target-supports-dg.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/target-supports-dg.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/target-supports-dg.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/target-supports-dg.exp
Looking for library file ../lib/scanasm.exp
Looking for library file /usr/share/dejagnu/scanasm.exp
Looking for library file /usr/share/dejagnu/lib/scanasm.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/scanasm.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scanasm.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scanasm.exp
Looking for library file ../lib/scanrtl.exp
Looking for library file /usr/share/dejagnu/scanrtl.exp
Looking for library file /usr/share/dejagnu/lib/scanrtl.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/scanrtl.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scanrtl.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scanrtl.exp
Looking for library file ../lib/scandump.exp
Looking for library file /usr/share/dejagnu/scandump.exp
Looking for library file /usr/share/dejagnu/lib/scandump.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/scandump.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scandump.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scandump.exp
Looking for library file ../lib/scantree.exp
Looking for library file /usr/share/dejagnu/scantree.exp
Looking for library file /usr/share/dejagnu/lib/scantree.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/scantree.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scantree.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scantree.exp
Looking for library file ../lib/scanltranstree.exp
Looking for library file /usr/share/dejagnu/scanltranstree.exp
Looking for library file /usr/share/dejagnu/lib/scanltranstree.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/scanltranstree.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scanltranstree.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scanltranstree.exp
Looking for library file ../lib/scanipa.exp
Looking for library file /usr/share/dejagnu/scanipa.exp
Looking for library file /usr/share/dejagnu/lib/scanipa.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/scanipa.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scanipa.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scanipa.exp
Looking for library file ../lib/scanwpaipa.exp
Looking for library file /usr/share/dejagnu/scanwpaipa.exp
Looking for library file /usr/share/dejagnu/lib/scanwpaipa.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/scanwpaipa.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scanwpaipa.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scanwpaipa.exp
Looking for library file ../lib/scanlang.exp
Looking for library file /usr/share/dejagnu/scanlang.exp
Looking for library file /usr/share/dejagnu/lib/scanlang.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/scanlang.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scanlang.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/scanlang.exp
Looking for library file ../lib/timeout-dg.exp
Looking for library file /usr/share/dejagnu/timeout-dg.exp
Looking for library file /usr/share/dejagnu/lib/timeout-dg.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/timeout-dg.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/timeout-dg.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/timeout-dg.exp
Looking for library file ../lib/torture-options.exp
Looking for library file /usr/share/dejagnu/torture-options.exp
Looking for library file /usr/share/dejagnu/lib/torture-options.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/torture-options.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/torture-options.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/torture-options.exp
Looking for library file ../lib/fortran-modules.exp
Looking for library file /usr/share/dejagnu/fortran-modules.exp
Looking for library file /usr/share/dejagnu/lib/fortran-modules.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/fortran-modules.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/fortran-modules.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/fortran-modules.exp
Checking pattern "*-*-cygwin*" with powerpc64le-unknown-linux-gnu
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/./xgcc
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/../xgcc
Checking "nvptx-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "amdgcn-*-*" against "powerpc64le-unknown-linux-gnu"
check_cached_effective_target lto: checking unix
check_compile tool: g++ for lto
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/././libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/../../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/../../../libio/_G_config.h
/home/luoxhu/workspace/../../../../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/././libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/../libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/../../libio/iostream.list
/home/luoxhu/workspace/gcc-git/../../../libio/iostream.list
/home/luoxhu/workspace/../../../../libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/./libio/Makefile.in
/home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/../libio/Makefile.in
/home/luoxhu/workspace/gcc-git/gcc-trunk/../../libio/Makefile.in
/home/luoxhu/workspace/gcc-git/../../../libio/Makefile.in
/home/luoxhu/workspace/../../../../libio/Makefile.in
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -nostdinc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/powerpc64le-unknown-linux-gnu -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/libsupc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/include/backward -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/testsuite/util -fmessage-length=0 -flto  -c -o lto115858.o lto115858.c
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -nostdinc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/powerpc64le-unknown-linux-gnu -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/libsupc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/include/backward -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/testsuite/util -fmessage-length=0 -flto  -c -o lto115858.o lto115858.c    (timeout = 300)
pid is 115920 -115920
waitres is 115920 exp6 0 0
output is  status 0
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
check_cached_effective_target lto: returning 1 for unix
check_compile tool: g++ for linker_plugin
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/././libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/../../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/../../../libio/_G_config.h
/home/luoxhu/workspace/../../../../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/././libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/../libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/../../libio/iostream.list
/home/luoxhu/workspace/gcc-git/../../../libio/iostream.list
/home/luoxhu/workspace/../../../../libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/./libio/Makefile.in
/home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/../libio/Makefile.in
/home/luoxhu/workspace/gcc-git/gcc-trunk/../../libio/Makefile.in
/home/luoxhu/workspace/gcc-git/../../../libio/Makefile.in
/home/luoxhu/workspace/../../../../libio/Makefile.in
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../ linker_plugin115858.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -nostdinc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/powerpc64le-unknown-linux-gnu -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/libsupc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/include/backward -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/testsuite/util -fmessage-length=0 -flto -fuse-linker-plugin    -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/ -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs -lm  -o linker_plugin115858.exe
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../ linker_plugin115858.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -nostdinc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/powerpc64le-unknown-linux-gnu -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/libsupc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/include/backward -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/testsuite/util -fmessage-length=0 -flto -fuse-linker-plugin    -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/ -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs -lm  -o linker_plugin115858.exe    (timeout = 300)
pid is 115927 -115927
waitres is 115927 exp6 0 0
output is  status 0
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
check_cached_effective_target llvm_binutils: checking unix
Checking "amdgcn*-*-*" against "powerpc64le-unknown-linux-gnu"
check_cached_effective_target offload_gcn: checking unix
check_compile tool: g++ for offload_gcn
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/././libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/../../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/../../../libio/_G_config.h
/home/luoxhu/workspace/../../../../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/././libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/../libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/../../libio/iostream.list
/home/luoxhu/workspace/gcc-git/../../../libio/iostream.list
/home/luoxhu/workspace/../../../../libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/./libio/Makefile.in
/home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/../libio/Makefile.in
/home/luoxhu/workspace/gcc-git/gcc-trunk/../../libio/Makefile.in
/home/luoxhu/workspace/gcc-git/../../../libio/Makefile.in
/home/luoxhu/workspace/../../../../libio/Makefile.in
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../ offload_gcn115858.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -nostdinc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/powerpc64le-unknown-linux-gnu -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/libsupc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/include/backward -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/testsuite/util -fmessage-length=0 -foffload=amdgcn-unknown-amdhsa  -S -o offload_gcn115858.s
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../ offload_gcn115858.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -nostdinc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/powerpc64le-unknown-linux-gnu -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/libsupc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/include/backward -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/testsuite/util -fmessage-length=0 -foffload=amdgcn-unknown-amdhsa  -S -o offload_gcn115858.s    (timeout = 300)
pid is 115945 -115945
waitres is 115945 exp6 0 1
close result is 115945 exp6 0 1
output is xg++: fatal error: GCC is not configured to support amdgcn-unknown-amdhsa as offload target
compilation terminated.
 status 1
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
compiler exited with status 1
output is:
xg++: fatal error: GCC is not configured to support amdgcn-unknown-amdhsa as offload target
compilation terminated.

check_cached_effective_target offload_gcn: returning 0 for unix
check_cached_effective_target llvm_binutils: returning 0 for unix
Testing g++.dg/tree-prof/crossmodule-indir-call-topn-3.C, 
Checking "nvptx-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "amdgcn-*-*" against "powerpc64le-unknown-linux-gnu"
check_cached_effective_target lto: returning 1 for unix
is-effective-target: lto 1
Profiling argument is <-fprofile-generate>
Checking "powerpc-ibm-aix*" against "powerpc64le-unknown-linux-gnu"
check_cached_effective_target tls_runtime: returning 1 for unix
Checking "*-*-solaris2*" against "powerpc64le-unknown-linux-gnu"
Checking "mips*-*-*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-mingw*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-cygwin*" against "powerpc64le-unknown-linux-gnu"
check_cached_effective_target uclibc: returning 0 for unix
check_cached_effective_target profiling_available: returning 1 for unix
check_compile tool: g++ for profiling
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/././libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/../../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/../../../libio/_G_config.h
/home/luoxhu/workspace/../../../../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/././libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/../libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/../../libio/iostream.list
/home/luoxhu/workspace/gcc-git/../../../libio/iostream.list
/home/luoxhu/workspace/../../../../libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/./libio/Makefile.in
/home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/../libio/Makefile.in
/home/luoxhu/workspace/gcc-git/gcc-trunk/../../libio/Makefile.in
/home/luoxhu/workspace/gcc-git/../../../libio/Makefile.in
/home/luoxhu/workspace/../../../../libio/Makefile.in
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../ profiling115858.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -nostdinc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/powerpc64le-unknown-linux-gnu -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/libsupc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/include/backward -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/testsuite/util -fmessage-length=0 -pg    -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/ -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs -lm  -o profiling115858.exe
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../ profiling115858.c    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -nostdinc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/powerpc64le-unknown-linux-gnu -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/libsupc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/include/backward -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/testsuite/util -fmessage-length=0 -pg    -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/ -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs -lm  -o profiling115858.exe    (timeout = 300)
pid is 115948 -115948
waitres is 115948 exp6 0 0
output is  status 0
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking "powerpc-ibm-aix*" against "powerpc64le-unknown-linux-gnu"
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/././libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/../../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/../../../libio/_G_config.h
/home/luoxhu/workspace/../../../../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/././libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/../libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/../../libio/iostream.list
/home/luoxhu/workspace/gcc-git/../../../libio/iostream.list
/home/luoxhu/workspace/../../../../libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/./libio/Makefile.in
/home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/../libio/Makefile.in
/home/luoxhu/workspace/gcc-git/gcc-trunk/../../libio/Makefile.in
/home/luoxhu/workspace/gcc-git/../../../libio/Makefile.in
/home/luoxhu/workspace/../../../../libio/Makefile.in
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/tree-prof/crossmodule-indir-call-topn-3.C    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -nostdinc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/powerpc64le-unknown-linux-gnu -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/libsupc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/include/backward -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/testsuite/util -fmessage-length=0  -O2 -DDOJOB=1 -fdump-ipa-profile_estimate -fno-early-inlining -fdump-ipa-all-details -fprofile-generate -D_PROFILE_GENERATE    -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/ -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/crossmodule-indir-call-topn-3.x01
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/tree-prof/crossmodule-indir-call-topn-3.C    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -nostdinc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/powerpc64le-unknown-linux-gnu -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/libsupc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/include/backward -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/testsuite/util -fmessage-length=0  -O2 -DDOJOB=1 -fdump-ipa-profile_estimate -fno-early-inlining -fdump-ipa-all-details -fprofile-generate -D_PROFILE_GENERATE    -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/ -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/crossmodule-indir-call-topn-3.x01    (timeout = 300)
pid is 115958 -115958
waitres is 115958 exp6 0 0
output is  status 0
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking powerpc64le-unknown-linux-gnu against powerpc64le-unknown-linux-gnu
loading to unix
Setting LD_LIBRARY_PATH to .:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../..:.:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../..:/home/luoxhu/local/lib/
spawning command /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/crossmodule-indir-call-topn-3.x01 
exp9 file6
Executed /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/crossmodule-indir-call-topn-3.x01, status 0
done:0

/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/././libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/../../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/../../../libio/_G_config.h
/home/luoxhu/workspace/../../../../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/././libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/../libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/../../libio/iostream.list
/home/luoxhu/workspace/gcc-git/../../../libio/iostream.list
/home/luoxhu/workspace/../../../../libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/./libio/Makefile.in
/home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/../libio/Makefile.in
/home/luoxhu/workspace/gcc-git/gcc-trunk/../../libio/Makefile.in
/home/luoxhu/workspace/gcc-git/../../../libio/Makefile.in
/home/luoxhu/workspace/../../../../libio/Makefile.in
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/tree-prof/crossmodule-indir-call-topn-3.C    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -nostdinc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/powerpc64le-unknown-linux-gnu -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/libsupc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/include/backward -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/testsuite/util -fmessage-length=0  -O2 -DDOJOB=1 -fdump-ipa-profile_estimate -fno-early-inlining -fdump-ipa-all-details -fprofile-use -D_PROFILE_USE    -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/ -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/crossmodule-indir-call-topn-3.x02
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/tree-prof/crossmodule-indir-call-topn-3.C    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -nostdinc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/powerpc64le-unknown-linux-gnu -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/libsupc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/include/backward -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/testsuite/util -fmessage-length=0  -O2 -DDOJOB=1 -fdump-ipa-profile_estimate -fno-early-inlining -fdump-ipa-all-details -fprofile-use -D_PROFILE_USE    -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/ -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/crossmodule-indir-call-topn-3.x02    (timeout = 300)
pid is 115971 -115971
waitres is 115971 exp6 0 0
output is  status 0
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking powerpc64le-unknown-linux-gnu against powerpc64le-unknown-linux-gnu
loading to unix
Setting LD_LIBRARY_PATH to .:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../..:.:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../..:/home/luoxhu/local/lib/
spawning command /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/crossmodule-indir-call-topn-3.x02 
exp9 file6
Executed /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/crossmodule-indir-call-topn-3.x02, status 0
done:0

g++.dg/tree-prof/crossmodule-indir-call-topn-3.C: dump file does not exist
dump file: crossmodule-indir-call-topn-3.x02.wpa.profile_estimate
remove-build-file `crossmodule-indir-call-topn-3.C.[0-9][0-9][0-9]{l,i,r,t}.*'
remove-build-file `crossmodule-indir-call-topn-3.C.016i.visibility crossmodule-indir-call-topn-3.C.077i.pure-const crossmodule-indir-call-topn-3.C.063i.free-fnsummary1 crossmodule-indir-call-topn-3.C.076i.inline crossmodule-indir-call-topn-3.C.064i.increase_alignment crossmodule-indir-call-topn-3.C.081i.comdats crossmodule-indir-call-topn-3.C.017i.build_ssa_passes crossmodule-indir-call-topn-3.C.067i.whole-program crossmodule-indir-call-topn-3.C.000i.ipa-clones crossmodule-indir-call-topn-3.C.047i.remove_symbols crossmodule-indir-call-topn-3.C.022i.opt_local_passes crossmodule-indir-call-topn-3.C.072i.sra crossmodule-indir-call-topn-3.C.059i.targetclone crossmodule-indir-call-topn-3.C.080i.single-use crossmodule-indir-call-topn-3.C.075i.fnsummary crossmodule-indir-call-topn-3.C.071i.cp crossmodule-indir-call-topn-3.C.078i.free-fnsummary2 crossmodule-indir-call-topn-3.C.069i.icf crossmodule-indir-call-topn-3.C.068i.profile_estimate crossmodule-indir-call-topn-3.C.079i.static-var crossmodule-indir-call-topn-3.C.082i.materialize-all-clones crossmodule-indir-call-topn-3.C.061i.profile crossmodule-indir-call-topn-3.C.000i.cgraph crossmodule-indir-call-topn-3.C.000i.type-inheritance crossmodule-indir-call-topn-3.C.070i.devirt'
Testing g++.dg/tree-prof/crossmodule-indir-call-topn-3a.C, 
Checking "powerpc-ibm-aix*" against "powerpc64le-unknown-linux-gnu"
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/././libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/../../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/../../../libio/_G_config.h
/home/luoxhu/workspace/../../../../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/././libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/../libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/../../libio/iostream.list
/home/luoxhu/workspace/gcc-git/../../../libio/iostream.list
/home/luoxhu/workspace/../../../../libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/./libio/Makefile.in
/home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/../libio/Makefile.in
/home/luoxhu/workspace/gcc-git/gcc-trunk/../../libio/Makefile.in
/home/luoxhu/workspace/gcc-git/../../../libio/Makefile.in
/home/luoxhu/workspace/../../../../libio/Makefile.in
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/tree-prof/crossmodule-indir-call-topn-3a.C    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -nostdinc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/powerpc64le-unknown-linux-gnu -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/libsupc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/include/backward -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/testsuite/util -fmessage-length=0   -fprofile-generate -D_PROFILE_GENERATE    -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/ -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/crossmodule-indir-call-topn-3a.x01
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/tree-prof/crossmodule-indir-call-topn-3a.C    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -nostdinc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/powerpc64le-unknown-linux-gnu -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/libsupc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/include/backward -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/testsuite/util -fmessage-length=0   -fprofile-generate -D_PROFILE_GENERATE    -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/ -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/crossmodule-indir-call-topn-3a.x01    (timeout = 300)
pid is 115994 -115994
waitres is 115994 exp6 0 0
output is  status 0
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking powerpc64le-unknown-linux-gnu against powerpc64le-unknown-linux-gnu
loading to unix
Setting LD_LIBRARY_PATH to .:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../..:.:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../..:/home/luoxhu/local/lib/
spawning command /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/crossmodule-indir-call-topn-3a.x01 
exp9 file6
Executed /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/crossmodule-indir-call-topn-3a.x01, status 0
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/././libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/../../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/../../../libio/_G_config.h
/home/luoxhu/workspace/../../../../libio/_G_config.h
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/././libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/../libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/../../libio/iostream.list
/home/luoxhu/workspace/gcc-git/../../../libio/iostream.list
/home/luoxhu/workspace/../../../../libio/iostream.list
/home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/./libio/Makefile.in
/home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/../libio/Makefile.in
/home/luoxhu/workspace/gcc-git/gcc-trunk/../../libio/Makefile.in
/home/luoxhu/workspace/gcc-git/../../../libio/Makefile.in
/home/luoxhu/workspace/../../../../libio/Makefile.in
doing compile
Invoking the compiler as /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/tree-prof/crossmodule-indir-call-topn-3a.C    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -nostdinc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/powerpc64le-unknown-linux-gnu -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/libsupc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/include/backward -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/testsuite/util -fmessage-length=0   -fprofile-use -D_PROFILE_USE    -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/ -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/crossmodule-indir-call-topn-3a.x02
Setting timeout to 300
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../ /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/tree-prof/crossmodule-indir-call-topn-3a.C    -fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never  -nostdinc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/powerpc64le-unknown-linux-gnu -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/libsupc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/include/backward -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/testsuite/util -fmessage-length=0   -fprofile-use -D_PROFILE_USE    -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/ -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs -lm  -o /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/crossmodule-indir-call-topn-3a.x02    (timeout = 300)
pid is 116054 -116054
waitres is 116054 exp6 0 0
output is  status 0
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking pattern "sparc-*-sunos*" with powerpc64le-unknown-linux-gnu
Checking pattern "alpha*-*-*" with powerpc64le-unknown-linux-gnu
Checking pattern "hppa*-*-hpux*" with powerpc64le-unknown-linux-gnu
Checking powerpc64le-unknown-linux-gnu against powerpc64le-unknown-linux-gnu
loading to unix
Setting LD_LIBRARY_PATH to .:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../..:.:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../..:/home/luoxhu/local/lib/
spawning command /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/crossmodule-indir-call-topn-3a.x02 
exp9 file6
Executed /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/crossmodule-indir-call-topn-3a.x02, status 0
Profiling argument is <-fauto-profile>
Checking "i?86-*-linux*" against "powerpc64le-unknown-linux-gnu"
Checking "x86_64-*-linux*" against "powerpc64le-unknown-linux-gnu"
autofdo only supported on linux
Profiling argument is <-fauto-profile>
Checking "i?86-*-linux*" against "powerpc64le-unknown-linux-gnu"
Checking "x86_64-*-linux*" against "powerpc64le-unknown-linux-gnu"
autofdo only supported on linux
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/tsan/tsan.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/ubsan/ubsan.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.dg/vect/vect.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.old-deja/old-deja.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.target/aarch64/aarch64.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.target/aarch64/sve/aarch64-sve.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.target/aarch64/sve/acle/aarch64-sve-acle-asm.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.target/aarch64/sve/acle/aarch64-sve-acle.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.target/aarch64/sve2/acle/aarch64-sve2-acle-asm.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.target/arm/arm.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.target/i386/i386.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.target/powerpc/powerpc.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/g++.target/riscv/riscv.exp in tree-prof.exp

		=== g++ Summary ===

# of expected passes		8
# of unresolved testcases	1
# of unsupported tests		2
Checking pattern "*-*-cygwin*" with powerpc64le-unknown-linux-gnu
Checking pattern "*-dos-*" with powerpc64le-unknown-linux-gnu
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/./xgcc
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/../xgcc
compiler is /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/./powerpc64le-unknown-linux-gnu
GCC path is .
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/././libstdc++-v3
Checking "*-*-darwin*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-cygwin*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-mingw*" against "powerpc64le-unknown-linux-gnu"
Checking "hppa*-*-hpux*" against "powerpc64le-unknown-linux-gnu"
shared lib extension: so
Checking "*-*-darwin*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-cygwin*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-mingw*" against "powerpc64le-unknown-linux-gnu"
Checking "hppa*-*-hpux*" against "powerpc64le-unknown-linux-gnu"
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../ -print-file-name=libgcc_s.so   (timeout = 300)
pid is 116117 -116117
waitres is 116117 exp6 0 0
output is /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../libgcc_s.so
 status 0
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../ -print-multi-directory   (timeout = 300)
pid is 116120 -116120
waitres is 116120 exp6 0 0
output is .
 status 0
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../ -print-multi-lib   (timeout = 300)
pid is 116123 -116123
waitres is 116123 exp6 0 0
output is .;
 status 0
Checking "*-*-cygwin*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-mingw*" against "powerpc64le-unknown-linux-gnu"
LD_LIBRARY_PATH=.:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../..:/home/luoxhu/local/lib/
LD_RUN_PATH=.:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../..
SHLIB_PATH=.:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../..
LD_LIBRARY_PATH_32=.:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../..:/home/luoxhu/local/lib/
LD_LIBRARY_PATH_64=.:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../..:/home/luoxhu/local/lib/
DYLD_LIBRARY_PATH=.:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../..
Checking "*-*-darwin*" against "powerpc64le-unknown-linux-gnu"
ALWAYS_CXXFLAGS set to {additional_flags=-fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never } {additional_flags=-nostdinc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include/powerpc64le-unknown-linux-gnu -I/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/libstdc++-v3/include -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/libsupc++ -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/include/backward -I/home/luoxhu/workspace/gcc-git/gcc-trunk/libstdc++-v3/testsuite/util} {ldflags= -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs  -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libstdc++-v3/src/.libs -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/ -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libitm/.libs} additional_flags=-fmessage-length=0
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++ -v    (timeout = 300)
pid is 116126 -116126
waitres is 116126 exp6 0 0
output is Using built-in specs.
COLLECT_GCC=/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++
COLLECT_LTO_WRAPPER=/home/luoxhu/local/gcc_10/libexec/gcc/powerpc64le-unknown-linux-gnu/10.0.0/lto-wrapper
Target: powerpc64le-unknown-linux-gnu
Configured with: ../gcc-trunk/configure --enable-languages=c,c++,fortran --prefix=/home/luoxhu/local/gcc_10/ --disable-libsanitizer --disable-bootstrap : (reconfigured) ../gcc-trunk/configure --prefix=/home/luoxhu/local/gcc_10/ --disable-libsanitizer --disable-bootstrap --enable-languages=c,c++,fortran,lto --no-create --no-recursion
Thread model: posix
Supported LTO compression algorithms: zlib
gcc version 10.0.0 20200114 (experimental) (GCC) 
 status 0
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/g++/../../xg++  version 10.0.0 20200114 (experimental) (GCC) 

runtest completed at Tue Jan 14 08:22:48 2020
make[2]: Leaving directory '/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc'
rm -rf testsuite/gfortran-parallel
make[2]: Entering directory '/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc'
(rootme=`${PWDCMD-pwd}`; export rootme; \
srcdir=`cd ../../gcc-trunk/gcc; ${PWDCMD-pwd}` ; export srcdir ; \
if [ -n "" ] \
   && [ -n "$GCC_RUNTEST_PARALLELIZE_DIR" ] \
   && [ -f testsuite/gfortran-parallel/finished ]; then \
  rm -rf testsuite/gfortran; \
else \
  cd testsuite/gfortran; \
  rm -f tmp-site.exp; \
  sed '/set tmpdir/ s|testsuite$|testsuite/gfortran|' \
	< ../../site.exp > tmp-site.exp; \
  /bin/sh ${srcdir}/../move-if-change tmp-site.exp site.exp; \
  EXPECT=`if [ -f ${rootme}/../expect/expect ] ; then echo ${rootme}/../expect/expect ; else echo expect ; fi` ; export EXPECT ; \
  if [ -f ${rootme}/../expect/expect ] ; then  \
    TCL_LIBRARY=`cd .. ; cd ${srcdir}/../tcl/library ; ${PWDCMD-pwd}` ; \
    export TCL_LIBRARY ; \
  fi ; \
  `if [ -f ${srcdir}/../dejagnu/runtest ] ; then echo ${srcdir}/../dejagnu/runtest ; else echo runtest; fi` --tool gfortran tree-prof.exp=crossmodule-* -v -v; \
  if [ -n "$GCC_RUNTEST_PARALLELIZE_DIR" ] ; then \
    touch ${rootme}/testsuite/gfortran-parallel/finished; \
  fi ; \
fi )
Expect binary is expect
Looking for /usr/share/dejagnu/runtest.exp.
Using /usr/share/dejagnu/runtest.exp as main test driver
Verbose level is 2
Login name is luoxhu
Looking for ~/.dejagnurc
Looking for /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gfortran/site.exp
Found /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gfortran/site.exp
Using test sources in /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite
Using test binaries in /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gfortran
Tool root directory is /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite
Using /usr/share/dejagnu to find libraries
Looking for /usr/share/dejagnu/site.exp
Found /usr/share/dejagnu/site.exp
Looking for library file ../lib/utils.exp
Looking for library file /usr/share/dejagnu/utils.exp
Loading /usr/share/dejagnu/utils.exp
Looking for library file ../lib/framework.exp
Looking for library file /usr/share/dejagnu/framework.exp
Loading /usr/share/dejagnu/framework.exp
Looking for library file ../lib/debugger.exp
Looking for library file /usr/share/dejagnu/debugger.exp
Loading /usr/share/dejagnu/debugger.exp
Looking for library file ../lib/remote.exp
Looking for library file /usr/share/dejagnu/remote.exp
Loading /usr/share/dejagnu/remote.exp
Looking for library file ../lib/telnet.exp
Looking for library file /usr/share/dejagnu/telnet.exp
Loading /usr/share/dejagnu/telnet.exp
Looking for library file ../lib/rlogin.exp
Looking for library file /usr/share/dejagnu/rlogin.exp
Loading /usr/share/dejagnu/rlogin.exp
Looking for library file ../lib/kermit.exp
Looking for library file /usr/share/dejagnu/kermit.exp
Loading /usr/share/dejagnu/kermit.exp
Looking for library file ../lib/tip.exp
Looking for library file /usr/share/dejagnu/tip.exp
Loading /usr/share/dejagnu/tip.exp
Looking for library file ../lib/rsh.exp
Looking for library file /usr/share/dejagnu/rsh.exp
Loading /usr/share/dejagnu/rsh.exp
Looking for library file ../lib/ftp.exp
Looking for library file /usr/share/dejagnu/ftp.exp
Loading /usr/share/dejagnu/ftp.exp
Looking for library file ../lib/target.exp
Looking for library file /usr/share/dejagnu/target.exp
Loading /usr/share/dejagnu/target.exp
Looking for library file ../lib/targetdb.exp
Looking for library file /usr/share/dejagnu/targetdb.exp
Loading /usr/share/dejagnu/targetdb.exp
Looking for library file ../lib/libgloss.exp
Looking for library file /usr/share/dejagnu/libgloss.exp
Loading /usr/share/dejagnu/libgloss.exp
Loading library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/gfortran.exp
Looking for library file ../lib/prune.exp
Looking for library file /usr/share/dejagnu/prune.exp
Looking for library file /usr/share/dejagnu/lib/prune.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/prune.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/prune.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/prune.exp
Looking for library file ../lib/multiline.exp
Looking for library file /usr/share/dejagnu/multiline.exp
Looking for library file /usr/share/dejagnu/lib/multiline.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/multiline.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/multiline.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/multiline.exp
Looking for library file ../lib/gcc-defs.exp
Looking for library file /usr/share/dejagnu/gcc-defs.exp
Looking for library file /usr/share/dejagnu/lib/gcc-defs.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/gcc-defs.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/gcc-defs.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/gcc-defs.exp
Looking for library file ../lib/target-libpath.exp
Looking for library file /usr/share/dejagnu/target-libpath.exp
Looking for library file /usr/share/dejagnu/lib/target-libpath.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/target-libpath.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/target-libpath.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/target-libpath.exp
Looking for library file ../lib/wrapper.exp
Looking for library file /usr/share/dejagnu/wrapper.exp
Looking for library file /usr/share/dejagnu/lib/wrapper.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/wrapper.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/wrapper.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/wrapper.exp
Looking for library file ../lib/target-utils.exp
Looking for library file /usr/share/dejagnu/target-utils.exp
Looking for library file /usr/share/dejagnu/lib/target-utils.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/target-utils.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/target-utils.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/target-utils.exp
Looking for library file ../lib/target-supports.exp
Looking for library file /usr/share/dejagnu/target-supports.exp
Looking for library file /usr/share/dejagnu/lib/target-supports.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/target-supports.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/target-supports.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/target-supports.exp
Looking for library file ../lib/timeout.exp
Looking for library file /usr/share/dejagnu/timeout.exp
Looking for library file /usr/share/dejagnu/lib/timeout.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/dejagnu/lib/timeout.exp
Looking for library file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/timeout.exp
Loading /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/lib/timeout.exp
Testing gfortran
Running only tests tree-prof.exp=crossmodule-*
Opening log files in .
Test Run By luoxhu on Tue Jan 14 08:22:49 2020
Checking powerpc64le-unknown-linux-gnu against powerpc64le-unknown-linux-gnu
Native configuration is powerpc64le-unknown-linux-gnu

		=== gfortran tests ===

setting trap for SIGTERM to terminated
setting trap for SIGINT to interrupted by user
setting trap for SIGQUIT to interrupted by user
dirlist is /usr/share/dejagnu/baseboards
Looking for standard board description file for build /usr/share/dejagnu/baseboards/standard.exp
Looking for board description file for build /usr/share/dejagnu/baseboards/genoa.exp
pushing config for build, name is genoa
dirlist is /usr/share/dejagnu/baseboards
Looking for standard board description file for host /usr/share/dejagnu/baseboards/standard.exp
Looking for board description file for host /usr/share/dejagnu/baseboards/genoa.exp
pushing config for host, name is genoa
Schedule of variations:
    unix

target is unix
Running target unix
Looking for tool-and-target-specific interface file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/config/base-config.exp
Looking for tool-and-target-specific interface file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/../config/base-config.exp
Looking for tool-and-target-specific interface file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/../../config/base-config.exp
Looking for tool-and-target-specific interface file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/../../../config/base-config.exp
dirlist is /usr/share/dejagnu/baseboards/genoa /usr/share/dejagnu/baseboards
Looking for standard board description file for target /usr/share/dejagnu/baseboards/genoa/standard.exp
Looking for standard board description file for target /usr/share/dejagnu/baseboards/standard.exp
Looking for board description file for target /usr/share/dejagnu/baseboards/genoa/unix.exp
Looking for board description file for target /usr/share/dejagnu/baseboards/unix.exp
Using /usr/share/dejagnu/baseboards/unix.exp as board description file for target.
Looking for generic interface file for target /usr/share/dejagnu/config/unix.exp
Using /usr/share/dejagnu/config/unix.exp as generic interface file for target.
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/./xgcc
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/../xgcc
Checking "*-*-cygwin*" against "powerpc64le-unknown-linux-gnu"
Checking "alpha*-*" against "powerpc64le-unknown-linux-gnu"
Checking "sparc64-*-linux-gnu" against "powerpc64le-unknown-linux-gnu"
Looking for tool-and-target-specific interface file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/config/unix.exp
Looking for tool-and-target-specific interface file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/config/gnu.exp
Looking for tool-and-target-specific interface file /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/config/default.exp
Using /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/config/default.exp as tool-and-target-specific interface file.
Looking for library file ../lib/standard.exp
Looking for library file /usr/share/dejagnu/standard.exp
Loading /usr/share/dejagnu/standard.exp
pushing config for target, name is unix
Top level testsuite dirs are /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gfortran.dg /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gfortran.fortran-torture
trying to glob /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gfortran.dg/coarray/caf.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gfortran.dg/debug/debug.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gfortran.dg/dg.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gfortran.dg/goacc/goacc.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gfortran.dg/gomp/gomp.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gfortran.dg/graphite/graphite.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gfortran.dg/guality/guality.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gfortran.dg/ieee/ieee.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gfortran.dg/lto/lto.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gfortran.dg/prof/prof.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gfortran.dg/vect/vect.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gfortran.fortran-torture/compile/compile.exp in tree-prof.exp
searching for /home/luoxhu/workspace/gcc-git/gcc-trunk/gcc/testsuite/gfortran.fortran-torture/execute/execute.exp in tree-prof.exp

		=== gfortran Summary ===

Checking pattern "*-*-cygwin*" with powerpc64le-unknown-linux-gnu
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/./xgcc
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/../xgcc
compiler is /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/xgcc
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/./powerpc64le-unknown-linux-gnu
GCC path is .
Seeing if /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gfortran/gfortran exists.
Didn't find file /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gfortran/gfortran, returning gfortran
Seeing if /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gfortran/../../gfortran exists.
Found file, returning /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gfortran/../../gfortran -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gfortran/../../ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libgfortran/
Checking "*-*-darwin*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-cygwin*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-mingw*" against "powerpc64le-unknown-linux-gnu"
Checking "hppa*-*-hpux*" against "powerpc64le-unknown-linux-gnu"
shared lib extension: so
Checking "*-*-darwin*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-cygwin*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-mingw*" against "powerpc64le-unknown-linux-gnu"
Checking "hppa*-*-hpux*" against "powerpc64le-unknown-linux-gnu"
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gfortran/../../gfortran -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gfortran/../../ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libgfortran/ -print-file-name=libgcc_s.so   (timeout = 300)
pid is 116254 -116254
waitres is 116254 exp6 0 0
output is /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gfortran/../../libgcc_s.so
 status 0
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gfortran/../../gfortran -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gfortran/../../ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libgfortran/ -print-multi-directory   (timeout = 300)
pid is 116258 -116258
waitres is 116258 exp6 0 0
output is .
 status 0
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gfortran/../../gfortran -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gfortran/../../ -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libgfortran/ -print-multi-lib   (timeout = 300)
pid is 116261 -116261
waitres is 116261 exp6 0 0
output is .;
 status 0
Checking "*-*-cygwin*" against "powerpc64le-unknown-linux-gnu"
Checking "*-*-mingw*" against "powerpc64le-unknown-linux-gnu"
LD_LIBRARY_PATH=.:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libgfortran/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libgfortran/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libatomic/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libquadmath/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libquadmath/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gfortran/../..:/home/luoxhu/local/lib/
LD_RUN_PATH=.:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libgfortran/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libgfortran/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libatomic/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libquadmath/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libquadmath/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gfortran/../..
SHLIB_PATH=.:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libgfortran/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libgfortran/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libatomic/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libquadmath/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libquadmath/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gfortran/../..
LD_LIBRARY_PATH_32=.:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libgfortran/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libgfortran/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libatomic/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libquadmath/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libquadmath/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gfortran/../..:/home/luoxhu/local/lib/
LD_LIBRARY_PATH_64=.:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libgfortran/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libgfortran/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libatomic/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libquadmath/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libquadmath/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gfortran/../..:/home/luoxhu/local/lib/
DYLD_LIBRARY_PATH=.:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libgfortran/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libgfortran/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libatomic/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libquadmath/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libquadmath/.libs:/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gfortran/../..
ALWAYS_GFORTRANFLAGS set to {additional_flags=-fno-diagnostics-show-caret -fno-diagnostics-show-line-numbers -fdiagnostics-color=never  -fdiagnostics-urls=never } {ldflags=-B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libgfortran/.libs -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libgfortran/.libs -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libgfortran/.libs -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libatomic/.libs -B/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libquadmath/.libs -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libquadmath/.libs -L/home/luoxhu/workspace/gcc-git/gcc-trunk_build/powerpc64le-unknown-linux-gnu/./libquadmath/.libs }
Executing on host: /home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gfortran/../../gfortran -v    (timeout = 300)
pid is 116264 -116264
waitres is 116264 exp6 0 0
output is Using built-in specs.
COLLECT_GCC=/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gfortran/../../gfortran
COLLECT_LTO_WRAPPER=/home/luoxhu/local/gcc_10/libexec/gcc/powerpc64le-unknown-linux-gnu/10.0.0/lto-wrapper
Target: powerpc64le-unknown-linux-gnu
Configured with: ../gcc-trunk/configure --enable-languages=c,c++,fortran --prefix=/home/luoxhu/local/gcc_10/ --disable-libsanitizer --disable-bootstrap : (reconfigured) ../gcc-trunk/configure --prefix=/home/luoxhu/local/gcc_10/ --disable-libsanitizer --disable-bootstrap --enable-languages=c,c++,fortran,lto --no-create --no-recursion
Thread model: posix
Supported LTO compression algorithms: zlib
gcc version 10.0.0 20200114 (experimental) (GCC) 
 status 0
/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc/testsuite/gfortran/../../gfortran  version 10.0.0 20200114 (experimental) (GCC) 

runtest completed at Tue Jan 14 08:22:49 2020
make[2]: Leaving directory '/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc'
make[1]: Leaving directory '/home/luoxhu/workspace/gcc-git/gcc-trunk_build/gcc'

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v7] Missed function specialization + partial devirtualization
  2020-01-13  8:47                                       ` luoxhu
  2020-01-14 13:37                                         ` Martin Liška
@ 2020-01-14 20:14                                         ` David Malcolm
  2020-01-18  0:07                                           ` [PATCH] ipa-profile.c: reset call_sums state within ipa-profile.c (PR ipa/93315) David Malcolm
  1 sibling, 1 reply; 38+ messages in thread
From: David Malcolm @ 2020-01-14 20:14 UTC (permalink / raw)
  To: luoxhu, Jan Hubicka; +Cc: Martin Liška, gcc-patches, segher, wschmidt

On Mon, 2020-01-13 at 11:23 +0800, luoxhu wrote:
> On 2020/1/10 19:08, Jan Hubicka wrote:
> > OK. You will need to do the obvious updates for Martin's patch
> > which turned some member functions into static functions.
> > 
> > Honza
> 
> Thanks a lot!  Rebased & updated, will commit below patch shortly
> when git push is ready.
> 
> 
> v8:
>  1. Rebase to master with Martin's static function (r280043) comments
> merge.
>     Boostrap/testsuite/SPEC2017 tested pass on Power8-LE.
>  2. TODO:
>     2.1. C++ devirt for multiple speculative call targets.
>     2.2. ipa-icf ipa_merge_profiles refine with COMDAT inline
> testcase.

[...]

> diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c

[...]

> +static ipa_profile_call_summaries *call_sums = NULL;

[...]
 
>  static void
>  ipa_profile_generate_summary (void)
> @@ -169,7 +261,10 @@ ipa_profile_generate_summary (void)
>    basic_block bb;
>  
>    hash_table<histogram_hash> hashtable (10);
> -  
> +
> +  gcc_checking_assert (!call_sums);
> +  call_sums = new ipa_profile_call_summaries (symtab);
> +

[...]

Unfortunately, this assertion is failing for most of the testcases in
jit.dg, reducing the number of PASS results in jit.sum from 10473 down
to 3254 in my builds.


Counter-intuitively, "jit" is not in --enable-languages=all (as it also
needs --enable-host-shared at configure time, which slows down the
built compiler code).


The jit code expects to be able to invoke the compiler code more than
once within the same process, purging all state.

It looks like this "call_sums" state needs deleting and resetting to
NULL after the compiler has run (or else we'll likely get an ICE due to
using old symtab/call summaries in subsequent in-process runs of the
compiler).

Is there a natural place to do that within the IPA hooks?  


Alternatively the following patch seems to fix things (not yet fully
tested though); hopefully it seems sane.

gcc/ChangeLog:
        * ipa-profile.c (ipa_profile_c_finalize): New function.
        * toplev.c (toplev::finalize): Call it.
        * toplev.h (ipa_profile_c_finalize): New decl.
---
 gcc/ipa-profile.c | 10 ++++++++++
 gcc/toplev.c      |  1 +
 gcc/toplev.h      |  2 ++
 3 files changed, 13 insertions(+)

diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
index fc231c916b7..699419ead19 100644
--- a/gcc/ipa-profile.c
+++ b/gcc/ipa-profile.c
@@ -1069,3 +1069,13 @@ make_pass_ipa_profile (gcc::context *ctxt)
 {
   return new pass_ipa_profile (ctxt);
 }
+
+/* Reset all state within ipa-profile.c so that we can rerun the compiler
+   within the same process.  For use by toplev::finalize.  */
+
+void
+ipa_profile_c_finalize ()
+{
+  delete call_sums;
+  call_sums = NULL;
+}
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 4c8be502c71..ca0515583c7 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -2453,6 +2453,7 @@ toplev::finalize (void)
   this_target_rtl->target_specific_initialized = false;
 
   /* Needs to be called before cgraph_c_finalize since it uses symtab.  */
+  ipa_profile_c_finalize ();
   ipa_reference_c_finalize ();
   ipa_fnsummary_c_finalize ();
 
diff --git a/gcc/toplev.h b/gcc/toplev.h
index d6c316962b0..10611691608 100644
--- a/gcc/toplev.h
+++ b/gcc/toplev.h
@@ -103,4 +103,6 @@ extern void parse_alignment_opts (void);
 
 extern void initialize_rtl (void);
 
+extern void ipa_profile_c_finalize (void);
+
 #endif /* ! GCC_TOPLEV_H */
-- 
2.21.0

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v7] Missed function specialization + partial devirtualization
  2020-01-14 14:55                                           ` luoxhu
@ 2020-01-15 10:44                                             ` Christophe Lyon
  2020-01-15 11:08                                               ` Martin Liška
  0 siblings, 1 reply; 38+ messages in thread
From: Christophe Lyon @ 2020-01-15 10:44 UTC (permalink / raw)
  To: luoxhu
  Cc: Martin Liška, Jan Hubicka, gcc Patches, Segher Boessenkool,
	Bill Schmidt

Hi,


On Tue, 14 Jan 2020 at 15:35, luoxhu <luoxhu@linux.ibm.com> wrote:
>
> Hi,
>
> On 2020/1/14 21:03, Martin Liška wrote:
> > On 1/13/20 4:23 AM, luoxhu wrote:
> >> Thanks a lot!  Rebased & updated, will commit below patch shortly when
> >> git push is ready.
> >
> > Hello.
> >
> > I'm pretty sure the patch contains failure of the following tests:
> >
> > FAIL: gcc.dg/tree-prof/crossmodule-indir-call-topn-1.c
> > scan-pgo-wpa-ipa-dump profile_estimate "2 \\(200.00%\\) speculations
> > produced."
> > FAIL: gcc.dg/tree-prof/crossmodule-indir-call-topn-2.c
> > scan-pgo-wpa-ipa-dump profile_estimate "2 \\(200.00%\\) speculations
> > produced."
> > FAIL: gcc.dg/tree-prof/crossmodule-indircall-1.c execution,
> > -fprofile-use -D_PROFILE_USE
> >
> > Can you please take a look?
>
> Thanks for reporting.
> This seems fails only on some platforms.  I tested it before commit and
> just verified it again on my Power8-LE with gcc.sum andd log attached.
> I suppose you are trying on x86 platforms and sorry that I don't have one
> at hand, these failures should be easy to fix, If not, I will investigate
> it after returning from Chinese New Year vacation.
>
It fails on arm and aarch64 too.

> make check-gcc RUNTESTFLAGS="tree-prof.exp=crossmodule-* -v -v" > log
>
> > Thanks,
> > Martin

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v7] Missed function specialization + partial devirtualization
  2020-01-15 10:44                                             ` Christophe Lyon
@ 2020-01-15 11:08                                               ` Martin Liška
  2020-01-15 13:17                                                 ` Martin Liška
  0 siblings, 1 reply; 38+ messages in thread
From: Martin Liška @ 2020-01-15 11:08 UTC (permalink / raw)
  To: Christophe Lyon, luoxhu
  Cc: Jan Hubicka, gcc Patches, Segher Boessenkool, Bill Schmidt

Hi.

I know the root cause of the failure and will send a patch soon
to the mailing list.

Martin

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v7] Missed function specialization + partial devirtualization
  2020-01-15 11:08                                               ` Martin Liška
@ 2020-01-15 13:17                                                 ` Martin Liška
  2020-01-15 13:19                                                   ` Jan Hubicka
  0 siblings, 1 reply; 38+ messages in thread
From: Martin Liška @ 2020-01-15 13:17 UTC (permalink / raw)
  To: Christophe Lyon, luoxhu
  Cc: Jan Hubicka, gcc Patches, Segher Boessenkool, Bill Schmidt

[-- Attachment #1: Type: text/plain, Size: 436 bytes --]

On 1/15/20 11:49 AM, Martin Liška wrote:
> Hi.
> 
> I know the root cause of the failure and will send a patch soon
> to the mailing list.
> 
> Martin

There's an obvious patch that I'm going to install. Note that
order of function call evaluation in a function call is undefined.
It's a similar issue we had some time ago in IPA VRP streaming.

Patch can bootstrap on x86_64-linux-gnu and survives regression tests.

Thanks,
Martin

[-- Attachment #2: 0001-Do-not-call-streamer_read_hwi-in-a-function-call.patch --]
[-- Type: text/x-patch, Size: 1074 bytes --]

From 674f2a3f4786fb61576c9152446937d617b1b4ce Mon Sep 17 00:00:00 2001
From: Martin Liska <mliska@suse.cz>
Date: Wed, 15 Jan 2020 11:49:48 +0100
Subject: [PATCH] Do not call streamer_read_hwi in a function call.

gcc/ChangeLog:

2020-01-15  Martin Liska  <mliska@suse.cz>

	* ipa-profile.c (ipa_profile_read_edge_summary): Do not allow
	2 calls of streamer_read_hwi in a function call.
---
 gcc/ipa-profile.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
index fc231c916b7..03272f20987 100644
--- a/gcc/ipa-profile.c
+++ b/gcc/ipa-profile.c
@@ -451,8 +451,9 @@ ipa_profile_read_edge_summary (class lto_input_block *ib, cgraph_edge *edge)
 
   for (i = 0; i < len; i++)
   {
-    speculative_call_target item (streamer_read_hwi (ib),
-	streamer_read_hwi (ib));
+    unsigned int target_id = streamer_read_hwi (ib);
+    int target_probability = streamer_read_hwi (ib);
+    speculative_call_target item (target_id, target_probability);
     csum->speculative_call_targets.safe_push (item);
   }
 }
-- 
2.24.1


^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v7] Missed function specialization + partial devirtualization
  2020-01-15 13:17                                                 ` Martin Liška
@ 2020-01-15 13:19                                                   ` Jan Hubicka
  2020-01-15 14:48                                                     ` Richard Biener
  0 siblings, 1 reply; 38+ messages in thread
From: Jan Hubicka @ 2020-01-15 13:19 UTC (permalink / raw)
  To: Martin Liška, dmalcolm
  Cc: Christophe Lyon, luoxhu, gcc Patches, Segher Boessenkool, Bill Schmidt

> gcc/ChangeLog:
> 
> 2020-01-15  Martin Liska  <mliska@suse.cz>
> 
> 	* ipa-profile.c (ipa_profile_read_edge_summary): Do not allow
> 	2 calls of streamer_read_hwi in a function call.

Good catch, Martin!
> ---
>  gcc/ipa-profile.c | 5 +++--
>  1 file changed, 3 insertions(+), 2 deletions(-)
> 
> diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
> index fc231c916b7..03272f20987 100644
> --- a/gcc/ipa-profile.c
> +++ b/gcc/ipa-profile.c
> @@ -451,8 +451,9 @@ ipa_profile_read_edge_summary (class lto_input_block *ib, cgraph_edge *edge)
>  
>    for (i = 0; i < len; i++)
>    {
> -    speculative_call_target item (streamer_read_hwi (ib),
> -	streamer_read_hwi (ib));
> +    unsigned int target_id = streamer_read_hwi (ib);
> +    int target_probability = streamer_read_hwi (ib);
> +    speculative_call_target item (target_id, target_probability);

David, this may be interesting case for analyzer/warning. This surfaced
in LTO stremaing for second time quite recently and leads to hard to
find bugs. 

While day before yesterday in pub I argued that warning for nested
function calls is not a good way since it would warn too often, perhaps
we want to have some way to annotate streamer functions to make it clear
that we do not want them to appear inside function call parameters where
evaluation order is undefined.

Honza
>      csum->speculative_call_targets.safe_push (item);
>    }
>  }
> -- 
> 2.24.1
> 

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v7] Missed function specialization + partial devirtualization
  2020-01-15 13:19                                                   ` Jan Hubicka
@ 2020-01-15 14:48                                                     ` Richard Biener
  2020-01-15 14:51                                                       ` Jan Hubicka
  0 siblings, 1 reply; 38+ messages in thread
From: Richard Biener @ 2020-01-15 14:48 UTC (permalink / raw)
  To: Jan Hubicka
  Cc: Martin Liška, David Malcolm, Christophe Lyon, luoxhu,
	gcc Patches, Segher Boessenkool, Bill Schmidt

On Wed, Jan 15, 2020 at 1:37 PM Jan Hubicka <hubicka@ucw.cz> wrote:
>
> > gcc/ChangeLog:
> >
> > 2020-01-15  Martin Liska  <mliska@suse.cz>
> >
> >       * ipa-profile.c (ipa_profile_read_edge_summary): Do not allow
> >       2 calls of streamer_read_hwi in a function call.
>
> Good catch, Martin!
> > ---
> >  gcc/ipa-profile.c | 5 +++--
> >  1 file changed, 3 insertions(+), 2 deletions(-)
> >
> > diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
> > index fc231c916b7..03272f20987 100644
> > --- a/gcc/ipa-profile.c
> > +++ b/gcc/ipa-profile.c
> > @@ -451,8 +451,9 @@ ipa_profile_read_edge_summary (class lto_input_block *ib, cgraph_edge *edge)
> >
> >    for (i = 0; i < len; i++)
> >    {
> > -    speculative_call_target item (streamer_read_hwi (ib),
> > -     streamer_read_hwi (ib));
> > +    unsigned int target_id = streamer_read_hwi (ib);
> > +    int target_probability = streamer_read_hwi (ib);
> > +    speculative_call_target item (target_id, target_probability);
>
> David, this may be interesting case for analyzer/warning. This surfaced
> in LTO stremaing for second time quite recently and leads to hard to
> find bugs.
>
> While day before yesterday in pub I argued that warning for nested
> function calls is not a good way since it would warn too often, perhaps
> we want to have some way to annotate streamer functions to make it clear
> that we do not want them to appear inside function call parameters where
> evaluation order is undefined.

Maybe one can always warn if you call the same function twice (and the
function appears to have side-effects)?  Might fire too often for tree checking
functions tho.

Richard.

> Honza
> >      csum->speculative_call_targets.safe_push (item);
> >    }
> >  }
> > --
> > 2.24.1
> >
>

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH v7] Missed function specialization + partial devirtualization
  2020-01-15 14:48                                                     ` Richard Biener
@ 2020-01-15 14:51                                                       ` Jan Hubicka
  0 siblings, 0 replies; 38+ messages in thread
From: Jan Hubicka @ 2020-01-15 14:51 UTC (permalink / raw)
  To: Richard Biener
  Cc: Martin Liška, David Malcolm, Christophe Lyon, luoxhu,
	gcc Patches, Segher Boessenkool, Bill Schmidt

> On Wed, Jan 15, 2020 at 1:37 PM Jan Hubicka <hubicka@ucw.cz> wrote:
> >
> > > gcc/ChangeLog:
> > >
> > > 2020-01-15  Martin Liska  <mliska@suse.cz>
> > >
> > >       * ipa-profile.c (ipa_profile_read_edge_summary): Do not allow
> > >       2 calls of streamer_read_hwi in a function call.
> >
> > Good catch, Martin!
> > > ---
> > >  gcc/ipa-profile.c | 5 +++--
> > >  1 file changed, 3 insertions(+), 2 deletions(-)
> > >
> > > diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
> > > index fc231c916b7..03272f20987 100644
> > > --- a/gcc/ipa-profile.c
> > > +++ b/gcc/ipa-profile.c
> > > @@ -451,8 +451,9 @@ ipa_profile_read_edge_summary (class lto_input_block *ib, cgraph_edge *edge)
> > >
> > >    for (i = 0; i < len; i++)
> > >    {
> > > -    speculative_call_target item (streamer_read_hwi (ib),
> > > -     streamer_read_hwi (ib));
> > > +    unsigned int target_id = streamer_read_hwi (ib);
> > > +    int target_probability = streamer_read_hwi (ib);
> > > +    speculative_call_target item (target_id, target_probability);
> >
> > David, this may be interesting case for analyzer/warning. This surfaced
> > in LTO stremaing for second time quite recently and leads to hard to
> > find bugs.
> >
> > While day before yesterday in pub I argued that warning for nested
> > function calls is not a good way since it would warn too often, perhaps
> > we want to have some way to annotate streamer functions to make it clear
> > that we do not want them to appear inside function call parameters where
> > evaluation order is undefined.
> 
> Maybe one can always warn if you call the same function twice (and the
> function appears to have side-effects)?  Might fire too often for tree checking
> functions tho.

Yep warning always when function calls appear within different
parameters of a function calls seems like something we would not be able
to enable by default...

Honza
> 
> Richard.
> 
> > Honza
> > >      csum->speculative_call_targets.safe_push (item);
> > >    }
> > >  }
> > > --
> > > 2.24.1
> > >
> >

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH] ipa-profile.c: reset call_sums state within ipa-profile.c (PR ipa/93315)
  2020-01-14 20:14                                         ` David Malcolm
@ 2020-01-18  0:07                                           ` David Malcolm
  2020-01-18 18:29                                             ` Jan Hubicka
  0 siblings, 1 reply; 38+ messages in thread
From: David Malcolm @ 2020-01-18  0:07 UTC (permalink / raw)
  To: Jan Hubicka, Martin Liška
  Cc: luoxhu, Christophe Lyon, Segher Boessenkool, Bill Schmidt,
	gcc-patches, David Malcolm

> > On Mon, 2020-01-13 at 11:23 +0800, luoxhu wrote:
> > On 2020/1/10 19:08, Jan Hubicka wrote:
> > > OK. You will need to do the obvious updates for Martin's patch
> > > which turned some member functions into static functions.
> > > 
> > > Honza
> > 
> > Thanks a lot!  Rebased & updated, will commit below patch shortly
> > when git push is ready.
> > 
> > 
> > v8:
> >  1. Rebase to master with Martin's static function (r280043) comments
> > merge.
> >     Boostrap/testsuite/SPEC2017 tested pass on Power8-LE.
> >  2. TODO:
> >     2.1. C++ devirt for multiple speculative call targets.
> >     2.2. ipa-icf ipa_merge_profiles refine with COMDAT inline
> > testcase.
> 
> [...]
> 
> > diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
> 
> [...]
> 
> > +static ipa_profile_call_summaries *call_sums = NULL;
> 
> [...]
>  
> >  static void
> >  ipa_profile_generate_summary (void)
> > @@ -169,7 +261,10 @@ ipa_profile_generate_summary (void)
> >    basic_block bb;
> >  
> >    hash_table<histogram_hash> hashtable (10);
> > -  
> > +
> > +  gcc_checking_assert (!call_sums);
> > +  call_sums = new ipa_profile_call_summaries (symtab);
> > +
> 
> [...]
> 
> Unfortunately, this assertion is failing for most of the testcases in
> jit.dg, reducing the number of PASS results in jit.sum from 10473 down
> to 3254 in my builds.
> 
> 
> Counter-intuitively, "jit" is not in --enable-languages=all (as it also
> needs --enable-host-shared at configure time, which slows down the
> built compiler code).
> 
> 
> The jit code expects to be able to invoke the compiler code more than
> once within the same process, purging all state.
> 
> It looks like this "call_sums" state needs deleting and resetting to
> NULL after the compiler has run (or else we'll likely get an ICE due to
> using old symtab/call summaries in subsequent in-process runs of the
> compiler).
> 
> Is there a natural place to do that within the IPA hooks?  
> 
> 
> Alternatively the following patch seems to fix things (not yet fully
> tested though); hopefully it seems sane.

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu; fixes
the issue with jit.sum.

OK for master?

gcc/ChangeLog:
	PR ipa/93315
	* ipa-profile.c (ipa_profile_c_finalize): New function.
	* toplev.c (toplev::finalize): Call it.
	* toplev.h (ipa_profile_c_finalize): New decl.
---
 gcc/ipa-profile.c | 10 ++++++++++
 gcc/toplev.c      |  1 +
 gcc/toplev.h      |  2 ++
 3 files changed, 13 insertions(+)

diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
index 03272f20987..5f7621b1432 100644
--- a/gcc/ipa-profile.c
+++ b/gcc/ipa-profile.c
@@ -1070,3 +1070,13 @@ make_pass_ipa_profile (gcc::context *ctxt)
 {
   return new pass_ipa_profile (ctxt);
 }
+
+/* Reset all state within ipa-profile.c so that we can rerun the compiler
+   within the same process.  For use by toplev::finalize.  */
+
+void
+ipa_profile_c_finalize ()
+{
+  delete call_sums;
+  call_sums = NULL;
+}
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 4c8be502c71..ca0515583c7 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -2453,6 +2453,7 @@ toplev::finalize (void)
   this_target_rtl->target_specific_initialized = false;
 
   /* Needs to be called before cgraph_c_finalize since it uses symtab.  */
+  ipa_profile_c_finalize ();
   ipa_reference_c_finalize ();
   ipa_fnsummary_c_finalize ();
 
diff --git a/gcc/toplev.h b/gcc/toplev.h
index d6c316962b0..10611691608 100644
--- a/gcc/toplev.h
+++ b/gcc/toplev.h
@@ -103,4 +103,6 @@ extern void parse_alignment_opts (void);
 
 extern void initialize_rtl (void);
 
+extern void ipa_profile_c_finalize (void);
+
 #endif /* ! GCC_TOPLEV_H */
-- 
2.21.0

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH] ipa-profile.c: reset call_sums state within ipa-profile.c (PR ipa/93315)
  2020-01-18  0:07                                           ` [PATCH] ipa-profile.c: reset call_sums state within ipa-profile.c (PR ipa/93315) David Malcolm
@ 2020-01-18 18:29                                             ` Jan Hubicka
  2020-01-21 14:23                                               ` [PATCH] ipa-profile.c: reset call_sums state within ipa-profile.c (v2; PR 93315) David Malcolm
  0 siblings, 1 reply; 38+ messages in thread
From: Jan Hubicka @ 2020-01-18 18:29 UTC (permalink / raw)
  To: David Malcolm
  Cc: Martin Liška, luoxhu, Christophe Lyon, Segher Boessenkool,
	Bill Schmidt, gcc-patches

> > > On Mon, 2020-01-13 at 11:23 +0800, luoxhu wrote:
> > > On 2020/1/10 19:08, Jan Hubicka wrote:
> > > > OK. You will need to do the obvious updates for Martin's patch
> > > > which turned some member functions into static functions.
> > > > 
> > > > Honza
> > > 
> > > Thanks a lot!  Rebased & updated, will commit below patch shortly
> > > when git push is ready.
> > > 
> > > 
> > > v8:
> > >  1. Rebase to master with Martin's static function (r280043) comments
> > > merge.
> > >     Boostrap/testsuite/SPEC2017 tested pass on Power8-LE.
> > >  2. TODO:
> > >     2.1. C++ devirt for multiple speculative call targets.
> > >     2.2. ipa-icf ipa_merge_profiles refine with COMDAT inline
> > > testcase.
> > 
> > [...]
> > 
> > > diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
> > 
> > [...]
> > 
> > > +static ipa_profile_call_summaries *call_sums = NULL;
> > 
> > [...]
> >  
> > >  static void
> > >  ipa_profile_generate_summary (void)
> > > @@ -169,7 +261,10 @@ ipa_profile_generate_summary (void)
> > >    basic_block bb;
> > >  
> > >    hash_table<histogram_hash> hashtable (10);
> > > -  
> > > +
> > > +  gcc_checking_assert (!call_sums);
> > > +  call_sums = new ipa_profile_call_summaries (symtab);
> > > +
> > 
> > [...]
> > 
> > Unfortunately, this assertion is failing for most of the testcases in
> > jit.dg, reducing the number of PASS results in jit.sum from 10473 down
> > to 3254 in my builds.
> > 
> > 
> > Counter-intuitively, "jit" is not in --enable-languages=all (as it also
> > needs --enable-host-shared at configure time, which slows down the
> > built compiler code).
> > 
> > 
> > The jit code expects to be able to invoke the compiler code more than
> > once within the same process, purging all state.
> > 
> > It looks like this "call_sums" state needs deleting and resetting to
> > NULL after the compiler has run (or else we'll likely get an ICE due to
> > using old symtab/call summaries in subsequent in-process runs of the
> > compiler).
> > 
> > Is there a natural place to do that within the IPA hooks?  
> > 
> > 
> > Alternatively the following patch seems to fix things (not yet fully
> > tested though); hopefully it seems sane.
> 
> Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu; fixes
> the issue with jit.sum.
> 
> OK for master?
> 
> gcc/ChangeLog:
> 	PR ipa/93315
> 	* ipa-profile.c (ipa_profile_c_finalize): New function.
> 	* toplev.c (toplev::finalize): Call it.
> 	* toplev.h (ipa_profile_c_finalize): New decl.

Other similar summaries are freed at the end of execute method.  I think
that we probably want to do the same for consistency as well.
Advantage is that this releases memory prior late compilation/streaming.

I think for all these summaries we have leak for -flto compilation where
we do not call execute methods and thus we do not free the summaries.
Is this problem for jit?

Honza

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH] ipa-profile.c: reset call_sums state within ipa-profile.c (v2; PR 93315)
  2020-01-18 18:29                                             ` Jan Hubicka
@ 2020-01-21 14:23                                               ` David Malcolm
  2020-01-21 15:18                                                 ` Jan Hubicka
  0 siblings, 1 reply; 38+ messages in thread
From: David Malcolm @ 2020-01-21 14:23 UTC (permalink / raw)
  To: Jan Hubicka
  Cc: Martin Liška, luoxhu, Christophe Lyon, Segher Boessenkool,
	Bill Schmidt, gcc-patches, David Malcolm

On Sat, 2020-01-18 at 18:42 +0100, Jan Hubicka wrote:
> > > > On Mon, 2020-01-13 at 11:23 +0800, luoxhu wrote:
> > > > On 2020/1/10 19:08, Jan Hubicka wrote:
> > > > > OK. You will need to do the obvious updates for Martin's
> > > > > patch
> > > > > which turned some member functions into static functions.
> > > > > 
> > > > > Honza
> > > > 
> > > > Thanks a lot!  Rebased & updated, will commit below patch
> > > > shortly
> > > > when git push is ready.
> > > > 
> > > > 
> > > > v8:
> > > >  1. Rebase to master with Martin's static function (r280043)
> > > > comments
> > > > merge.
> > > >     Boostrap/testsuite/SPEC2017 tested pass on Power8-LE.
> > > >  2. TODO:
> > > >     2.1. C++ devirt for multiple speculative call targets.
> > > >     2.2. ipa-icf ipa_merge_profiles refine with COMDAT inline
> > > > testcase.
> > > 
> > > [...]
> > > 
> > > > diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
> > > 
> > > [...]
> > > 
> > > > +static ipa_profile_call_summaries *call_sums = NULL;
> > > 
> > > [...]
> > >  
> > > >  static void
> > > >  ipa_profile_generate_summary (void)
> > > > @@ -169,7 +261,10 @@ ipa_profile_generate_summary (void)
> > > >    basic_block bb;
> > > >  
> > > >    hash_table<histogram_hash> hashtable (10);
> > > > -  
> > > > +
> > > > +  gcc_checking_assert (!call_sums);
> > > > +  call_sums = new ipa_profile_call_summaries (symtab);
> > > > +
> > > 
> > > [...]
> > > 
> > > Unfortunately, this assertion is failing for most of the
> > > testcases in
> > > jit.dg, reducing the number of PASS results in jit.sum from 10473
> > > down
> > > to 3254 in my builds.
> > > 
> > > 
> > > Counter-intuitively, "jit" is not in --enable-languages=all (as
> > > it also
> > > needs --enable-host-shared at configure time, which slows down
> > > the
> > > built compiler code).
> > > 
> > > 
> > > The jit code expects to be able to invoke the compiler code more
> > > than
> > > once within the same process, purging all state.
> > > 
> > > It looks like this "call_sums" state needs deleting and resetting
> > > to
> > > NULL after the compiler has run (or else we'll likely get an ICE
> > > due to
> > > using old symtab/call summaries in subsequent in-process runs of
> > > the
> > > compiler).
> > > 
> > > Is there a natural place to do that within the IPA hooks?  
> > > 
> > > 
> > > Alternatively the following patch seems to fix things (not yet
> > > fully
> > > tested though); hopefully it seems sane.
> > 
> > Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu;
> > fixes
> > the issue with jit.sum.
> > 
> > OK for master?
> > 
> > gcc/ChangeLog:
> > 	PR ipa/93315
> > 	* ipa-profile.c (ipa_profile_c_finalize): New function.
> > 	* toplev.c (toplev::finalize): Call it.
> > 	* toplev.h (ipa_profile_c_finalize): New decl.
> 
> Other similar summaries are freed at the end of execute method.  I
> think
> that we probably want to do the same for consistency as well.
> Advantage is that this releases memory prior late
> compilation/streaming.

Thanks; here's an updated, much simpler patch, which does it at the
end of ipa_profile (which is effectively the execute method).

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu;
fixes the issue with jit.sum:

Changes to jit.sum
------------------

  FAIL: 69->0 (-69)
  PASS: 3254->10471 (+7217)
  UNRESOLVED: 1->0 (-1)

OK for master?

> I think for all these summaries we have leak for -flto compilation
> where
> we do not call execute methods and thus we do not free the summaries.
> Is this problem for jit?
> 
> Honza

If we do, then, if I understand correctly, this would only affect
someone who tried to use libgccjit to generate .o files with -flto,
repeatedly, within a single process.  I don't know of anyone doing
that, and if that's broken, that would be a separate, pre-existing,
bug, I think.

Dave

gcc/ChangeLog:
	PR ipa/93315
	* ipa-profile.c (ipa_profile): Delete call_sums and set it to
	NULL on exit.
---
 gcc/ipa-profile.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
index 03272f20987..a69ba0c373a 100644
--- a/gcc/ipa-profile.c
+++ b/gcc/ipa-profile.c
@@ -1023,6 +1023,9 @@ ipa_profile (void)
   if (dump_file && (dump_flags & TDF_DETAILS))
     symtab->dump (dump_file);
 
+  delete call_sums;
+  call_sums = NULL;
+
   return 0;
 }
 
-- 
2.21.0

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH] ipa-profile.c: reset call_sums state within ipa-profile.c (v2; PR 93315)
  2020-01-21 14:23                                               ` [PATCH] ipa-profile.c: reset call_sums state within ipa-profile.c (v2; PR 93315) David Malcolm
@ 2020-01-21 15:18                                                 ` Jan Hubicka
  2020-01-21 15:34                                                   ` David Malcolm
  0 siblings, 1 reply; 38+ messages in thread
From: Jan Hubicka @ 2020-01-21 15:18 UTC (permalink / raw)
  To: David Malcolm
  Cc: Martin Liška, luoxhu, Christophe Lyon, Segher Boessenkool,
	Bill Schmidt, gcc-patches

> 
> If we do, then, if I understand correctly, this would only affect
> someone who tried to use libgccjit to generate .o files with -flto,
> repeatedly, within a single process.  I don't know of anyone doing
> that, and if that's broken, that would be a separate, pre-existing,
> bug, I think.

Yes, i think we can play with that incrementally especially if someone
tries to use -flto with JIT setup (which by itself looks like bit of
overkill but perhaps things like offloading or so could make this
meaningful).

Honza
> 
> Dave
> 
> gcc/ChangeLog:
> 	PR ipa/93315
> 	* ipa-profile.c (ipa_profile): Delete call_sums and set it to
> 	NULL on exit.
> ---
>  gcc/ipa-profile.c | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
> index 03272f20987..a69ba0c373a 100644
> --- a/gcc/ipa-profile.c
> +++ b/gcc/ipa-profile.c
> @@ -1023,6 +1023,9 @@ ipa_profile (void)
>    if (dump_file && (dump_flags & TDF_DETAILS))
>      symtab->dump (dump_file);
>  
> +  delete call_sums;
> +  call_sums = NULL;
> +
>    return 0;
>  }
>  
> -- 
> 2.21.0
> 

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH] ipa-profile.c: reset call_sums state within ipa-profile.c (v2; PR 93315)
  2020-01-21 15:18                                                 ` Jan Hubicka
@ 2020-01-21 15:34                                                   ` David Malcolm
  2020-01-21 15:39                                                     ` Jan Hubicka
  0 siblings, 1 reply; 38+ messages in thread
From: David Malcolm @ 2020-01-21 15:34 UTC (permalink / raw)
  To: Jan Hubicka
  Cc: Martin Liška, luoxhu, Christophe Lyon, Segher Boessenkool,
	Bill Schmidt, gcc-patches

On Tue, 2020-01-21 at 16:10 +0100, Jan Hubicka wrote:
> > If we do, then, if I understand correctly, this would only affect
> > someone who tried to use libgccjit to generate .o files with -flto,
> > repeatedly, within a single process.  I don't know of anyone doing
> > that, and if that's broken, that would be a separate, pre-existing,
> > bug, I think.
> 
> Yes, i think we can play with that incrementally especially if
> someone
> tries to use -flto with JIT setup (which by itself looks like bit of
> overkill but perhaps things like offloading or so could make this
> meaningful).
> 
> Honza
> > Dave

Thanks.  Is the patch OK?

> > gcc/ChangeLog:
> > 	PR ipa/93315
> > 	* ipa-profile.c (ipa_profile): Delete call_sums and set it to
> > 	NULL on exit.
> > ---
> >  gcc/ipa-profile.c | 3 +++
> >  1 file changed, 3 insertions(+)
> > 
> > diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
> > index 03272f20987..a69ba0c373a 100644
> > --- a/gcc/ipa-profile.c
> > +++ b/gcc/ipa-profile.c
> > @@ -1023,6 +1023,9 @@ ipa_profile (void)
> >    if (dump_file && (dump_flags & TDF_DETAILS))
> >      symtab->dump (dump_file);
> >  
> > +  delete call_sums;
> > +  call_sums = NULL;
> > +
> >    return 0;
> >  }
> >  
> > -- 
> > 2.21.0
> > 

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH] ipa-profile.c: reset call_sums state within ipa-profile.c (v2; PR 93315)
  2020-01-21 15:34                                                   ` David Malcolm
@ 2020-01-21 15:39                                                     ` Jan Hubicka
  0 siblings, 0 replies; 38+ messages in thread
From: Jan Hubicka @ 2020-01-21 15:39 UTC (permalink / raw)
  To: David Malcolm
  Cc: Martin Liška, luoxhu, Christophe Lyon, Segher Boessenkool,
	Bill Schmidt, gcc-patches

> On Tue, 2020-01-21 at 16:10 +0100, Jan Hubicka wrote:
> > > If we do, then, if I understand correctly, this would only affect
> > > someone who tried to use libgccjit to generate .o files with -flto,
> > > repeatedly, within a single process.  I don't know of anyone doing
> > > that, and if that's broken, that would be a separate, pre-existing,
> > > bug, I think.
> > 
> > Yes, i think we can play with that incrementally especially if
> > someone
> > tries to use -flto with JIT setup (which by itself looks like bit of
> > overkill but perhaps things like offloading or so could make this
> > meaningful).
> > 
> > Honza
> > > Dave
> 
> Thanks.  Is the patch OK?
Yes, i meant to apporve it :)
Thanks for looking into this.

Honza
> 
> > > gcc/ChangeLog:
> > > 	PR ipa/93315
> > > 	* ipa-profile.c (ipa_profile): Delete call_sums and set it to
> > > 	NULL on exit.
> > > ---
> > >  gcc/ipa-profile.c | 3 +++
> > >  1 file changed, 3 insertions(+)
> > > 
> > > diff --git a/gcc/ipa-profile.c b/gcc/ipa-profile.c
> > > index 03272f20987..a69ba0c373a 100644
> > > --- a/gcc/ipa-profile.c
> > > +++ b/gcc/ipa-profile.c
> > > @@ -1023,6 +1023,9 @@ ipa_profile (void)
> > >    if (dump_file && (dump_flags & TDF_DETAILS))
> > >      symtab->dump (dump_file);
> > >  
> > > +  delete call_sums;
> > > +  call_sums = NULL;
> > > +
> > >    return 0;
> > >  }
> > >  
> > > -- 
> > > 2.21.0
> > > 
> 

^ permalink raw reply	[flat|nested] 38+ messages in thread

end of thread, other threads:[~2020-01-21 15:34 UTC | newest]

Thread overview: 38+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-07-12  9:19 [PATCH v2] Missed function specialization + partial devirtualization Xiong Hu Luo
2019-07-15  8:20 ` Martin Liška
2019-07-31  5:51   ` [PATCH v3] " luoxhu
2019-08-13 13:16     ` Martin Liška
2019-08-13 13:21       ` Martin Liška
2019-09-25  3:46       ` [PATCH v4] " luoxhu
2019-09-25 10:57         ` Martin Liška
2019-09-26  5:24           ` luoxhu
2019-09-26  8:36             ` Martin Liška
2019-09-27  7:13               ` [PATCH v5] " luoxhu
2019-10-16  3:33                 ` Ping: " luoxhu
2019-10-22 14:24                 ` Martin Liška
2019-11-06  5:06                   ` Ping: " luoxhu
2019-11-14  6:01                     ` Ping*2: " luoxhu
2019-11-14  9:15                       ` Jan Hubicka
2019-11-16 10:14                         ` luoxhu
2019-11-18 13:04                           ` Martin Liška
2019-12-03  2:31                             ` [PATCH v6] " luoxhu
2019-12-18  2:33                               ` *Ping* " luoxhu
2019-12-19 13:22                                 ` Jan Hubicka
2019-12-26 13:42                                   ` [PATCH v7] " luoxhu
2020-01-10 11:23                                     ` Jan Hubicka
2020-01-13  8:47                                       ` luoxhu
2020-01-14 13:37                                         ` Martin Liška
2020-01-14 14:55                                           ` luoxhu
2020-01-15 10:44                                             ` Christophe Lyon
2020-01-15 11:08                                               ` Martin Liška
2020-01-15 13:17                                                 ` Martin Liška
2020-01-15 13:19                                                   ` Jan Hubicka
2020-01-15 14:48                                                     ` Richard Biener
2020-01-15 14:51                                                       ` Jan Hubicka
2020-01-14 20:14                                         ` David Malcolm
2020-01-18  0:07                                           ` [PATCH] ipa-profile.c: reset call_sums state within ipa-profile.c (PR ipa/93315) David Malcolm
2020-01-18 18:29                                             ` Jan Hubicka
2020-01-21 14:23                                               ` [PATCH] ipa-profile.c: reset call_sums state within ipa-profile.c (v2; PR 93315) David Malcolm
2020-01-21 15:18                                                 ` Jan Hubicka
2020-01-21 15:34                                                   ` David Malcolm
2020-01-21 15:39                                                     ` Jan Hubicka

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).