[3/6] ipa: Bundle estimates of ipa_call_context::estimate_size_and_time

Message ID 3e71f4974dee623dfa99fa6722b9d0fc5eae3c99.1601403165.git.mjambor@suse.cz
State New
Headers show
Series
  • IPA cleanups and IPA-CP improvements for 548.exchange2_r
Related show

Commit Message

Martin Jambor Sept. 28, 2020, 6:47 p.m.
A subsequent patch adds another two estimates that the code in
ipa_call_context::estimate_size_and_time computes, and the fact that
the function has a special output parameter for each thing it computes
would make it have just too many.  Therefore, this patch collapses all
those ouptut parameters into one output structure.

gcc/ChangeLog:

2020-09-02  Martin Jambor  <mjambor@suse.cz>

	* ipa-inline-analysis.c (do_estimate_edge_time): Adjusted to use
	ipa_call_estimates.
	(do_estimate_edge_size): Likewise.
	(do_estimate_edge_hints): Likewise.
	* ipa-fnsummary.h (struct ipa_call_estimates): New type.
	(ipa_call_context::estimate_size_and_time): Adjusted declaration.
	(estimate_ipcp_clone_size_and_time): Likewise.
	* ipa-cp.c (hint_time_bonus): Changed the type of the second argument
	to ipa_call_estimates.
	(perform_estimation_of_a_value): Adjusted to use ipa_call_estimates.
	(estimate_local_effects): Likewise.
	* ipa-fnsummary.c (ipa_call_context::estimate_size_and_time): Adjusted
	to return estimates in a single ipa_call_estimates parameter.
	(estimate_ipcp_clone_size_and_time): Likewise.
---
 gcc/ipa-cp.c              | 45 ++++++++++++++---------------
 gcc/ipa-fnsummary.c       | 60 +++++++++++++++++++--------------------
 gcc/ipa-fnsummary.h       | 36 +++++++++++++++++------
 gcc/ipa-inline-analysis.c | 47 +++++++++++++++++-------------
 4 files changed, 105 insertions(+), 83 deletions(-)

-- 
2.28.0

Comments

Jan Hubicka Sept. 29, 2020, 6:39 p.m. | #1
> A subsequent patch adds another two estimates that the code in

> ipa_call_context::estimate_size_and_time computes, and the fact that

> the function has a special output parameter for each thing it computes

> would make it have just too many.  Therefore, this patch collapses all

> those ouptut parameters into one output structure.

> 

> gcc/ChangeLog:

> 

> 2020-09-02  Martin Jambor  <mjambor@suse.cz>

> 

> 	* ipa-inline-analysis.c (do_estimate_edge_time): Adjusted to use

> 	ipa_call_estimates.

> 	(do_estimate_edge_size): Likewise.

> 	(do_estimate_edge_hints): Likewise.

> 	* ipa-fnsummary.h (struct ipa_call_estimates): New type.

> 	(ipa_call_context::estimate_size_and_time): Adjusted declaration.

> 	(estimate_ipcp_clone_size_and_time): Likewise.

> 	* ipa-cp.c (hint_time_bonus): Changed the type of the second argument

> 	to ipa_call_estimates.

> 	(perform_estimation_of_a_value): Adjusted to use ipa_call_estimates.

> 	(estimate_local_effects): Likewise.

> 	* ipa-fnsummary.c (ipa_call_context::estimate_size_and_time): Adjusted

> 	to return estimates in a single ipa_call_estimates parameter.

> 	(estimate_ipcp_clone_size_and_time): Likewise.

OK,
Honza
> ---

>  gcc/ipa-cp.c              | 45 ++++++++++++++---------------

>  gcc/ipa-fnsummary.c       | 60 +++++++++++++++++++--------------------

>  gcc/ipa-fnsummary.h       | 36 +++++++++++++++++------

>  gcc/ipa-inline-analysis.c | 47 +++++++++++++++++-------------

>  4 files changed, 105 insertions(+), 83 deletions(-)

> 

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

> index 292dd7e5bdf..77c84a6ed5d 100644

> --- a/gcc/ipa-cp.c

> +++ b/gcc/ipa-cp.c

> @@ -3196,12 +3196,13 @@ devirtualization_time_bonus (struct cgraph_node *node,

>    return res;

>  }

>  

> -/* Return time bonus incurred because of HINTS.  */

> +/* Return time bonus incurred because of hints stored in ESTIMATES.  */

>  

>  static int

> -hint_time_bonus (cgraph_node *node, ipa_hints hints)

> +hint_time_bonus (cgraph_node *node, const ipa_call_estimates &estimates)

>  {

>    int result = 0;

> +  ipa_hints hints = estimates.hints;

>    if (hints & (INLINE_HINT_loop_iterations | INLINE_HINT_loop_stride))

>      result += opt_for_fn (node->decl, param_ipa_cp_loop_hint_bonus);

>    return result;

> @@ -3397,15 +3398,13 @@ perform_estimation_of_a_value (cgraph_node *node,

>  			       int removable_params_cost, int est_move_cost,

>  			       ipcp_value_base *val)

>  {

> -  int size, time_benefit;

> -  sreal time, base_time;

> -  ipa_hints hints;

> +  int time_benefit;

> +  ipa_call_estimates estimates;

>  

> -  estimate_ipcp_clone_size_and_time (node, avals, &size, &time,

> -				     &base_time, &hints);

> -  base_time -= time;

> -  if (base_time > 65535)

> -    base_time = 65535;

> +  estimate_ipcp_clone_size_and_time (node, avals, &estimates);

> +  sreal time_delta = estimates.nonspecialized_time - estimates.time;

> +  if (time_delta > 65535)

> +    time_delta = 65535;

>  

>    /* Extern inline functions have no cloning local time benefits because they

>       will be inlined anyway.  The only reason to clone them is if it enables

> @@ -3413,11 +3412,12 @@ perform_estimation_of_a_value (cgraph_node *node,

>    if (DECL_EXTERNAL (node->decl) && DECL_DECLARED_INLINE_P (node->decl))

>      time_benefit = 0;

>    else

> -    time_benefit = base_time.to_int ()

> +    time_benefit = time_delta.to_int ()

>        + devirtualization_time_bonus (node, avals)

> -      + hint_time_bonus (node, hints)

> +      + hint_time_bonus (node, estimates)

>        + removable_params_cost + est_move_cost;

>  

> +  int size = estimates.size;

>    gcc_checking_assert (size >=0);

>    /* The inliner-heuristics based estimates may think that in certain

>       contexts some functions do not have any size at all but we want

> @@ -3472,23 +3472,21 @@ estimate_local_effects (struct cgraph_node *node)

>        || (removable_params_cost && node->can_change_signature))

>      {

>        struct caller_statistics stats;

> -      ipa_hints hints;

> -      sreal time, base_time;

> -      int size;

> +      ipa_call_estimates estimates;

>  

>        init_caller_stats (&stats);

>        node->call_for_symbol_thunks_and_aliases (gather_caller_stats, &stats,

>  					      false);

> -      estimate_ipcp_clone_size_and_time (node, &avals, &size, &time,

> -					 &base_time, &hints);

> -      time -= devirt_bonus;

> -      time -= hint_time_bonus (node, hints);

> -      time -= removable_params_cost;

> -      size -= stats.n_calls * removable_params_cost;

> +      estimate_ipcp_clone_size_and_time (node, &avals, &estimates);

> +      sreal time = estimates.nonspecialized_time - estimates.time;

> +      time += devirt_bonus;

> +      time += hint_time_bonus (node, estimates);

> +      time += removable_params_cost;

> +      int size = estimates.size - stats.n_calls * removable_params_cost;

>  

>        if (dump_file)

>  	fprintf (dump_file, " - context independent values, size: %i, "

> -		 "time_benefit: %f\n", size, (base_time - time).to_double ());

> +		 "time_benefit: %f\n", size, (time).to_double ());

>  

>        if (size <= 0 || node->local)

>  	{

> @@ -3499,8 +3497,7 @@ estimate_local_effects (struct cgraph_node *node)

>  		     "known contexts, code not going to grow.\n");

>  	}

>        else if (good_cloning_opportunity_p (node,

> -					   MIN ((base_time - time).to_int (),

> -						65536),

> +					   MIN ((time).to_int (), 65536),

>  					   stats.freq_sum, stats.count_sum,

>  					   size))

>  	{

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

> index 4ef7d2570e9..6082f34d63f 100644

> --- a/gcc/ipa-fnsummary.c

> +++ b/gcc/ipa-fnsummary.c

> @@ -3536,18 +3536,14 @@ ipa_call_context::equal_to (const ipa_call_context &ctx)

>    return true;

>  }

>  

> -/* Estimate size and time needed to execute call in the given context.

> -   Additionally determine hints determined by the context.  Finally compute

> -   minimal size needed for the call that is independent on the call context and

> -   can be used for fast estimates.  Return the values in RET_SIZE,

> -   RET_MIN_SIZE, RET_TIME and RET_HINTS.  */

> +/* Fill in the selected fields in ESTIMATES with value estimated for call in

> +   this context.  Always compute size and min_size.  Only compute time and

> +   nonspecialized_time if EST_TIMES is true.  Only compute hints if EST_HINTS

> +   is true.  */

>  

>  void

> -ipa_call_context::estimate_size_and_time (int *ret_size,

> -					  int *ret_min_size,

> -					  sreal *ret_time,

> -					  sreal *ret_nonspecialized_time,

> -					  ipa_hints *ret_hints)

> +ipa_call_context::estimate_size_and_time (ipa_call_estimates *estimates,

> +					  bool est_times, bool est_hints)

>  {

>    class ipa_fn_summary *info = ipa_fn_summaries->get (m_node);

>    size_time_entry *e;

> @@ -3577,8 +3573,8 @@ ipa_call_context::estimate_size_and_time (int *ret_size,

>  

>    if (m_node->callees || m_node->indirect_calls)

>      estimate_calls_size_and_time (m_node, &size, &min_size,

> -				  ret_time ? &time : NULL,

> -				  ret_hints ? &hints : NULL, m_possible_truths,

> +				  est_times ? &time : NULL,

> +				  est_hints ? &hints : NULL, m_possible_truths,

>  				  &m_avals);

>  

>    sreal nonspecialized_time = time;

> @@ -3605,7 +3601,7 @@ ipa_call_context::estimate_size_and_time (int *ret_size,

>  	     known to be constant in a specialized setting.  */

>  	  if (nonconst)

>  	    size += e->size;

> -	  if (!ret_time)

> +	  if (!est_times)

>  	    continue;

>  	  nonspecialized_time += e->time;

>  	  if (!nonconst)

> @@ -3645,7 +3641,7 @@ ipa_call_context::estimate_size_and_time (int *ret_size,

>    if (time > nonspecialized_time)

>      time = nonspecialized_time;

>  

> -  if (ret_hints)

> +  if (est_hints)

>      {

>        if (info->loop_iterations

>  	  && !info->loop_iterations->evaluate (m_possible_truths))

> @@ -3663,18 +3659,23 @@ ipa_call_context::estimate_size_and_time (int *ret_size,

>    min_size = RDIV (min_size, ipa_fn_summary::size_scale);

>  

>    if (dump_file && (dump_flags & TDF_DETAILS))

> -    fprintf (dump_file, "\n   size:%i time:%f nonspec time:%f\n", (int) size,

> -	     time.to_double (), nonspecialized_time.to_double ());

> -  if (ret_time)

> -    *ret_time = time;

> -  if (ret_nonspecialized_time)

> -    *ret_nonspecialized_time = nonspecialized_time;

> -  if (ret_size)

> -    *ret_size = size;

> -  if (ret_min_size)

> -    *ret_min_size = min_size;

> -  if (ret_hints)

> -    *ret_hints = hints;

> +    {

> +      if (est_times)

> +	fprintf (dump_file, "\n   size:%i time:%f nonspec time:%f\n",

> +		 (int) size, time.to_double (),

> +		 nonspecialized_time.to_double ());

> +      else

> +	fprintf (dump_file, "\n   size:%i (time not estimated)\n", (int) size);

> +    }

> +  if (est_times)

> +    {

> +      estimates->time = time;

> +      estimates->nonspecialized_time = nonspecialized_time;

> +    }

> +  estimates->size = size;

> +  estimates->min_size = min_size;

> +  if (est_hints)

> +    estimates->hints = hints;

>    return;

>  }

>  

> @@ -3687,17 +3688,14 @@ ipa_call_context::estimate_size_and_time (int *ret_size,

>  void

>  estimate_ipcp_clone_size_and_time (struct cgraph_node *node,

>  				   ipa_auto_call_arg_values *avals,

> -				   int *ret_size, sreal *ret_time,

> -				   sreal *ret_nonspec_time,

> -				   ipa_hints *hints)

> +				   ipa_call_estimates *estimates)

>  {

>    clause_t clause, nonspec_clause;

>  

>    evaluate_conditions_for_known_args (node, false, avals, &clause,

>  				      &nonspec_clause);

>    ipa_call_context ctx (node, clause, nonspec_clause, vNULL, avals);

> -  ctx.estimate_size_and_time (ret_size, NULL, ret_time,

> -			      ret_nonspec_time, hints);

> +  ctx.estimate_size_and_time (estimates);

>  }

>  

>  /* Return stack frame offset where frame of NODE is supposed to start inside

> diff --git a/gcc/ipa-fnsummary.h b/gcc/ipa-fnsummary.h

> index 020a6f0425d..ccb6b432f0b 100644

> --- a/gcc/ipa-fnsummary.h

> +++ b/gcc/ipa-fnsummary.h

> @@ -287,6 +287,29 @@ public:

>  			  ipa_call_summary *dst_data);

>  };

>  

> +/* Estimated execution times, code sizes and other information about the

> +   code executing a call described by ipa_call_context.  */

> +

> +struct ipa_call_estimates

> +{

> +  /* Estimated size needed to execute call in the given context. */

> +  int size;

> +

> +  /* Minimal size needed for the call that is + independent on the call context

> +     and can be used for fast estimates.  */

> +  int min_size;

> +

> +  /* Estimated time needed to execute call in the given context. */

> +  sreal time;

> +

> +  /* Estimated time needed to execute the function when not ignoring

> +     computations known to be constant in this context.  */

> +  sreal nonspecialized_time;

> +

> +  /* Further discovered reasons why to inline or specialize the give calls.  */

> +  ipa_hints hints;

> +};

> +

>  class ipa_cached_call_context;

>  

>  /* This object describe a context of call.  That is a summary of known

> @@ -305,10 +328,8 @@ public:

>    : m_node(NULL)

>    {

>    }

> -  void estimate_size_and_time (int *ret_size, int *ret_min_size,

> -			       sreal *ret_time,

> -			       sreal *ret_nonspecialized_time,

> -			       ipa_hints *ret_hints);

> +  void estimate_size_and_time (ipa_call_estimates *estimates,

> +			       bool est_times = true, bool est_hints = true);

>    bool equal_to (const ipa_call_context &);

>    bool exists_p ()

>    {

> @@ -353,10 +374,9 @@ void ipa_dump_hints (FILE *f, ipa_hints);

>  void ipa_free_fn_summary (void);

>  void ipa_free_size_summary (void);

>  void inline_analyze_function (struct cgraph_node *node);

> -void estimate_ipcp_clone_size_and_time (struct cgraph_node *,

> -					ipa_auto_call_arg_values *,

> -					int *, sreal *, sreal *,

> -				        ipa_hints *);

> +void estimate_ipcp_clone_size_and_time (struct cgraph_node *node,

> +					ipa_auto_call_arg_values *avals,

> +					ipa_call_estimates *estimates);

>  void ipa_merge_fn_summary_after_inlining (struct cgraph_edge *edge);

>  void ipa_update_overall_fn_summary (struct cgraph_node *node, bool reset = true);

>  void compute_fn_summary (struct cgraph_node *, bool);

> diff --git a/gcc/ipa-inline-analysis.c b/gcc/ipa-inline-analysis.c

> index b7af77f7b9b..acbf82e84d9 100644

> --- a/gcc/ipa-inline-analysis.c

> +++ b/gcc/ipa-inline-analysis.c

> @@ -208,16 +208,12 @@ do_estimate_edge_time (struct cgraph_edge *edge, sreal *ret_nonspec_time)

>  	      && !opt_for_fn (callee->decl, flag_profile_partial_training)

>  	      && !callee->count.ipa_p ())

>  	    {

> -	      sreal chk_time, chk_nonspec_time;

> -	      int chk_size, chk_min_size;

> -

> -	      ipa_hints chk_hints;

> -	      ctx.estimate_size_and_time (&chk_size, &chk_min_size,

> -					  &chk_time, &chk_nonspec_time,

> -					  &chk_hints);

> -	      gcc_assert (chk_size == size && chk_time == time

> -		  	  && chk_nonspec_time == nonspec_time

> -			  && chk_hints == hints);

> +	      ipa_call_estimates chk_estimates;

> +	      ctx.estimate_size_and_time (&chk_estimates);

> +	      gcc_assert (chk_estimates.size == size

> +			  && chk_estimates.time == time

> +		  	  && chk_estimates.nonspecialized_time == nonspec_time

> +			  && chk_estimates.hints == hints);

>  	    }

>  	}

>        else

> @@ -227,18 +223,28 @@ do_estimate_edge_time (struct cgraph_edge *edge, sreal *ret_nonspec_time)

>  	  else

>  	    node_context_cache_clear++;

>  	  e->entry.ctx.release ();

> -	  ctx.estimate_size_and_time (&size, &min_size,

> -				      &time, &nonspec_time, &hints);

> +	  ipa_call_estimates estimates;

> +	  ctx.estimate_size_and_time (&estimates);

> +	  size = estimates.size;

>  	  e->entry.size = size;

> +	  time = estimates.time;

>  	  e->entry.time = time;

> +	  nonspec_time = estimates.nonspecialized_time;

>  	  e->entry.nonspec_time = nonspec_time;

> +	  hints = estimates.hints;

>  	  e->entry.hints = hints;

>  	  e->entry.ctx.duplicate_from (ctx);

>  	}

>      }

>    else

> -    ctx.estimate_size_and_time (&size, &min_size,

> -				&time, &nonspec_time, &hints);

> +    {

> +      ipa_call_estimates estimates;

> +      ctx.estimate_size_and_time (&estimates);

> +      size = estimates.size;

> +      time = estimates.time;

> +      nonspec_time = estimates.nonspecialized_time;

> +      hints = estimates.hints;

> +    }

>  

>    /* When we have profile feedback, we can quite safely identify hot

>       edges and for those we disable size limits.  Don't do that when

> @@ -321,8 +327,9 @@ do_estimate_edge_size (struct cgraph_edge *edge)

>    evaluate_properties_for_edge (edge, true, &clause, &nonspec_clause,

>  				&avals, true);

>    ipa_call_context ctx (callee, clause, nonspec_clause, vNULL, &avals);

> -  ctx.estimate_size_and_time (&size, NULL, NULL, NULL, NULL);

> -  return size;

> +  ipa_call_estimates estimates;

> +  ctx.estimate_size_and_time (&estimates, false, false);

> +  return estimates.size;

>  }

>  

>  

> @@ -332,7 +339,6 @@ do_estimate_edge_size (struct cgraph_edge *edge)

>  ipa_hints

>  do_estimate_edge_hints (struct cgraph_edge *edge)

>  {

> -  ipa_hints hints;

>    struct cgraph_node *callee;

>    clause_t clause, nonspec_clause;

>  

> @@ -341,7 +347,7 @@ do_estimate_edge_hints (struct cgraph_edge *edge)

>    if (edge_growth_cache != NULL)

>      {

>        do_estimate_edge_time (edge);

> -      hints = edge_growth_cache->get (edge)->hints;

> +      ipa_hints hints = edge_growth_cache->get (edge)->hints;

>        gcc_checking_assert (hints);

>        return hints - 1;

>      }

> @@ -354,8 +360,9 @@ do_estimate_edge_hints (struct cgraph_edge *edge)

>    evaluate_properties_for_edge (edge, true, &clause, &nonspec_clause,

>  				&avals, true);

>    ipa_call_context ctx (callee, clause, nonspec_clause, vNULL, &avals);

> -  ctx.estimate_size_and_time (NULL, NULL, NULL, NULL, &hints);

> -  hints |= simple_edge_hints (edge);

> +  ipa_call_estimates estimates;

> +  ctx.estimate_size_and_time (&estimates, false, true);

> +  ipa_hints hints = estimates.hints | simple_edge_hints (edge);

>    return hints;

>  }

>  

> -- 

> 2.28.0

>

Patch

diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c
index 292dd7e5bdf..77c84a6ed5d 100644
--- a/gcc/ipa-cp.c
+++ b/gcc/ipa-cp.c
@@ -3196,12 +3196,13 @@  devirtualization_time_bonus (struct cgraph_node *node,
   return res;
 }
 
-/* Return time bonus incurred because of HINTS.  */
+/* Return time bonus incurred because of hints stored in ESTIMATES.  */
 
 static int
-hint_time_bonus (cgraph_node *node, ipa_hints hints)
+hint_time_bonus (cgraph_node *node, const ipa_call_estimates &estimates)
 {
   int result = 0;
+  ipa_hints hints = estimates.hints;
   if (hints & (INLINE_HINT_loop_iterations | INLINE_HINT_loop_stride))
     result += opt_for_fn (node->decl, param_ipa_cp_loop_hint_bonus);
   return result;
@@ -3397,15 +3398,13 @@  perform_estimation_of_a_value (cgraph_node *node,
 			       int removable_params_cost, int est_move_cost,
 			       ipcp_value_base *val)
 {
-  int size, time_benefit;
-  sreal time, base_time;
-  ipa_hints hints;
+  int time_benefit;
+  ipa_call_estimates estimates;
 
-  estimate_ipcp_clone_size_and_time (node, avals, &size, &time,
-				     &base_time, &hints);
-  base_time -= time;
-  if (base_time > 65535)
-    base_time = 65535;
+  estimate_ipcp_clone_size_and_time (node, avals, &estimates);
+  sreal time_delta = estimates.nonspecialized_time - estimates.time;
+  if (time_delta > 65535)
+    time_delta = 65535;
 
   /* Extern inline functions have no cloning local time benefits because they
      will be inlined anyway.  The only reason to clone them is if it enables
@@ -3413,11 +3412,12 @@  perform_estimation_of_a_value (cgraph_node *node,
   if (DECL_EXTERNAL (node->decl) && DECL_DECLARED_INLINE_P (node->decl))
     time_benefit = 0;
   else
-    time_benefit = base_time.to_int ()
+    time_benefit = time_delta.to_int ()
       + devirtualization_time_bonus (node, avals)
-      + hint_time_bonus (node, hints)
+      + hint_time_bonus (node, estimates)
       + removable_params_cost + est_move_cost;
 
+  int size = estimates.size;
   gcc_checking_assert (size >=0);
   /* The inliner-heuristics based estimates may think that in certain
      contexts some functions do not have any size at all but we want
@@ -3472,23 +3472,21 @@  estimate_local_effects (struct cgraph_node *node)
       || (removable_params_cost && node->can_change_signature))
     {
       struct caller_statistics stats;
-      ipa_hints hints;
-      sreal time, base_time;
-      int size;
+      ipa_call_estimates estimates;
 
       init_caller_stats (&stats);
       node->call_for_symbol_thunks_and_aliases (gather_caller_stats, &stats,
 					      false);
-      estimate_ipcp_clone_size_and_time (node, &avals, &size, &time,
-					 &base_time, &hints);
-      time -= devirt_bonus;
-      time -= hint_time_bonus (node, hints);
-      time -= removable_params_cost;
-      size -= stats.n_calls * removable_params_cost;
+      estimate_ipcp_clone_size_and_time (node, &avals, &estimates);
+      sreal time = estimates.nonspecialized_time - estimates.time;
+      time += devirt_bonus;
+      time += hint_time_bonus (node, estimates);
+      time += removable_params_cost;
+      int size = estimates.size - stats.n_calls * removable_params_cost;
 
       if (dump_file)
 	fprintf (dump_file, " - context independent values, size: %i, "
-		 "time_benefit: %f\n", size, (base_time - time).to_double ());
+		 "time_benefit: %f\n", size, (time).to_double ());
 
       if (size <= 0 || node->local)
 	{
@@ -3499,8 +3497,7 @@  estimate_local_effects (struct cgraph_node *node)
 		     "known contexts, code not going to grow.\n");
 	}
       else if (good_cloning_opportunity_p (node,
-					   MIN ((base_time - time).to_int (),
-						65536),
+					   MIN ((time).to_int (), 65536),
 					   stats.freq_sum, stats.count_sum,
 					   size))
 	{
diff --git a/gcc/ipa-fnsummary.c b/gcc/ipa-fnsummary.c
index 4ef7d2570e9..6082f34d63f 100644
--- a/gcc/ipa-fnsummary.c
+++ b/gcc/ipa-fnsummary.c
@@ -3536,18 +3536,14 @@  ipa_call_context::equal_to (const ipa_call_context &ctx)
   return true;
 }
 
-/* Estimate size and time needed to execute call in the given context.
-   Additionally determine hints determined by the context.  Finally compute
-   minimal size needed for the call that is independent on the call context and
-   can be used for fast estimates.  Return the values in RET_SIZE,
-   RET_MIN_SIZE, RET_TIME and RET_HINTS.  */
+/* Fill in the selected fields in ESTIMATES with value estimated for call in
+   this context.  Always compute size and min_size.  Only compute time and
+   nonspecialized_time if EST_TIMES is true.  Only compute hints if EST_HINTS
+   is true.  */
 
 void
-ipa_call_context::estimate_size_and_time (int *ret_size,
-					  int *ret_min_size,
-					  sreal *ret_time,
-					  sreal *ret_nonspecialized_time,
-					  ipa_hints *ret_hints)
+ipa_call_context::estimate_size_and_time (ipa_call_estimates *estimates,
+					  bool est_times, bool est_hints)
 {
   class ipa_fn_summary *info = ipa_fn_summaries->get (m_node);
   size_time_entry *e;
@@ -3577,8 +3573,8 @@  ipa_call_context::estimate_size_and_time (int *ret_size,
 
   if (m_node->callees || m_node->indirect_calls)
     estimate_calls_size_and_time (m_node, &size, &min_size,
-				  ret_time ? &time : NULL,
-				  ret_hints ? &hints : NULL, m_possible_truths,
+				  est_times ? &time : NULL,
+				  est_hints ? &hints : NULL, m_possible_truths,
 				  &m_avals);
 
   sreal nonspecialized_time = time;
@@ -3605,7 +3601,7 @@  ipa_call_context::estimate_size_and_time (int *ret_size,
 	     known to be constant in a specialized setting.  */
 	  if (nonconst)
 	    size += e->size;
-	  if (!ret_time)
+	  if (!est_times)
 	    continue;
 	  nonspecialized_time += e->time;
 	  if (!nonconst)
@@ -3645,7 +3641,7 @@  ipa_call_context::estimate_size_and_time (int *ret_size,
   if (time > nonspecialized_time)
     time = nonspecialized_time;
 
-  if (ret_hints)
+  if (est_hints)
     {
       if (info->loop_iterations
 	  && !info->loop_iterations->evaluate (m_possible_truths))
@@ -3663,18 +3659,23 @@  ipa_call_context::estimate_size_and_time (int *ret_size,
   min_size = RDIV (min_size, ipa_fn_summary::size_scale);
 
   if (dump_file && (dump_flags & TDF_DETAILS))
-    fprintf (dump_file, "\n   size:%i time:%f nonspec time:%f\n", (int) size,
-	     time.to_double (), nonspecialized_time.to_double ());
-  if (ret_time)
-    *ret_time = time;
-  if (ret_nonspecialized_time)
-    *ret_nonspecialized_time = nonspecialized_time;
-  if (ret_size)
-    *ret_size = size;
-  if (ret_min_size)
-    *ret_min_size = min_size;
-  if (ret_hints)
-    *ret_hints = hints;
+    {
+      if (est_times)
+	fprintf (dump_file, "\n   size:%i time:%f nonspec time:%f\n",
+		 (int) size, time.to_double (),
+		 nonspecialized_time.to_double ());
+      else
+	fprintf (dump_file, "\n   size:%i (time not estimated)\n", (int) size);
+    }
+  if (est_times)
+    {
+      estimates->time = time;
+      estimates->nonspecialized_time = nonspecialized_time;
+    }
+  estimates->size = size;
+  estimates->min_size = min_size;
+  if (est_hints)
+    estimates->hints = hints;
   return;
 }
 
@@ -3687,17 +3688,14 @@  ipa_call_context::estimate_size_and_time (int *ret_size,
 void
 estimate_ipcp_clone_size_and_time (struct cgraph_node *node,
 				   ipa_auto_call_arg_values *avals,
-				   int *ret_size, sreal *ret_time,
-				   sreal *ret_nonspec_time,
-				   ipa_hints *hints)
+				   ipa_call_estimates *estimates)
 {
   clause_t clause, nonspec_clause;
 
   evaluate_conditions_for_known_args (node, false, avals, &clause,
 				      &nonspec_clause);
   ipa_call_context ctx (node, clause, nonspec_clause, vNULL, avals);
-  ctx.estimate_size_and_time (ret_size, NULL, ret_time,
-			      ret_nonspec_time, hints);
+  ctx.estimate_size_and_time (estimates);
 }
 
 /* Return stack frame offset where frame of NODE is supposed to start inside
diff --git a/gcc/ipa-fnsummary.h b/gcc/ipa-fnsummary.h
index 020a6f0425d..ccb6b432f0b 100644
--- a/gcc/ipa-fnsummary.h
+++ b/gcc/ipa-fnsummary.h
@@ -287,6 +287,29 @@  public:
 			  ipa_call_summary *dst_data);
 };
 
+/* Estimated execution times, code sizes and other information about the
+   code executing a call described by ipa_call_context.  */
+
+struct ipa_call_estimates
+{
+  /* Estimated size needed to execute call in the given context. */
+  int size;
+
+  /* Minimal size needed for the call that is + independent on the call context
+     and can be used for fast estimates.  */
+  int min_size;
+
+  /* Estimated time needed to execute call in the given context. */
+  sreal time;
+
+  /* Estimated time needed to execute the function when not ignoring
+     computations known to be constant in this context.  */
+  sreal nonspecialized_time;
+
+  /* Further discovered reasons why to inline or specialize the give calls.  */
+  ipa_hints hints;
+};
+
 class ipa_cached_call_context;
 
 /* This object describe a context of call.  That is a summary of known
@@ -305,10 +328,8 @@  public:
   : m_node(NULL)
   {
   }
-  void estimate_size_and_time (int *ret_size, int *ret_min_size,
-			       sreal *ret_time,
-			       sreal *ret_nonspecialized_time,
-			       ipa_hints *ret_hints);
+  void estimate_size_and_time (ipa_call_estimates *estimates,
+			       bool est_times = true, bool est_hints = true);
   bool equal_to (const ipa_call_context &);
   bool exists_p ()
   {
@@ -353,10 +374,9 @@  void ipa_dump_hints (FILE *f, ipa_hints);
 void ipa_free_fn_summary (void);
 void ipa_free_size_summary (void);
 void inline_analyze_function (struct cgraph_node *node);
-void estimate_ipcp_clone_size_and_time (struct cgraph_node *,
-					ipa_auto_call_arg_values *,
-					int *, sreal *, sreal *,
-				        ipa_hints *);
+void estimate_ipcp_clone_size_and_time (struct cgraph_node *node,
+					ipa_auto_call_arg_values *avals,
+					ipa_call_estimates *estimates);
 void ipa_merge_fn_summary_after_inlining (struct cgraph_edge *edge);
 void ipa_update_overall_fn_summary (struct cgraph_node *node, bool reset = true);
 void compute_fn_summary (struct cgraph_node *, bool);
diff --git a/gcc/ipa-inline-analysis.c b/gcc/ipa-inline-analysis.c
index b7af77f7b9b..acbf82e84d9 100644
--- a/gcc/ipa-inline-analysis.c
+++ b/gcc/ipa-inline-analysis.c
@@ -208,16 +208,12 @@  do_estimate_edge_time (struct cgraph_edge *edge, sreal *ret_nonspec_time)
 	      && !opt_for_fn (callee->decl, flag_profile_partial_training)
 	      && !callee->count.ipa_p ())
 	    {
-	      sreal chk_time, chk_nonspec_time;
-	      int chk_size, chk_min_size;
-
-	      ipa_hints chk_hints;
-	      ctx.estimate_size_and_time (&chk_size, &chk_min_size,
-					  &chk_time, &chk_nonspec_time,
-					  &chk_hints);
-	      gcc_assert (chk_size == size && chk_time == time
-		  	  && chk_nonspec_time == nonspec_time
-			  && chk_hints == hints);
+	      ipa_call_estimates chk_estimates;
+	      ctx.estimate_size_and_time (&chk_estimates);
+	      gcc_assert (chk_estimates.size == size
+			  && chk_estimates.time == time
+		  	  && chk_estimates.nonspecialized_time == nonspec_time
+			  && chk_estimates.hints == hints);
 	    }
 	}
       else
@@ -227,18 +223,28 @@  do_estimate_edge_time (struct cgraph_edge *edge, sreal *ret_nonspec_time)
 	  else
 	    node_context_cache_clear++;
 	  e->entry.ctx.release ();
-	  ctx.estimate_size_and_time (&size, &min_size,
-				      &time, &nonspec_time, &hints);
+	  ipa_call_estimates estimates;
+	  ctx.estimate_size_and_time (&estimates);
+	  size = estimates.size;
 	  e->entry.size = size;
+	  time = estimates.time;
 	  e->entry.time = time;
+	  nonspec_time = estimates.nonspecialized_time;
 	  e->entry.nonspec_time = nonspec_time;
+	  hints = estimates.hints;
 	  e->entry.hints = hints;
 	  e->entry.ctx.duplicate_from (ctx);
 	}
     }
   else
-    ctx.estimate_size_and_time (&size, &min_size,
-				&time, &nonspec_time, &hints);
+    {
+      ipa_call_estimates estimates;
+      ctx.estimate_size_and_time (&estimates);
+      size = estimates.size;
+      time = estimates.time;
+      nonspec_time = estimates.nonspecialized_time;
+      hints = estimates.hints;
+    }
 
   /* When we have profile feedback, we can quite safely identify hot
      edges and for those we disable size limits.  Don't do that when
@@ -321,8 +327,9 @@  do_estimate_edge_size (struct cgraph_edge *edge)
   evaluate_properties_for_edge (edge, true, &clause, &nonspec_clause,
 				&avals, true);
   ipa_call_context ctx (callee, clause, nonspec_clause, vNULL, &avals);
-  ctx.estimate_size_and_time (&size, NULL, NULL, NULL, NULL);
-  return size;
+  ipa_call_estimates estimates;
+  ctx.estimate_size_and_time (&estimates, false, false);
+  return estimates.size;
 }
 
 
@@ -332,7 +339,6 @@  do_estimate_edge_size (struct cgraph_edge *edge)
 ipa_hints
 do_estimate_edge_hints (struct cgraph_edge *edge)
 {
-  ipa_hints hints;
   struct cgraph_node *callee;
   clause_t clause, nonspec_clause;
 
@@ -341,7 +347,7 @@  do_estimate_edge_hints (struct cgraph_edge *edge)
   if (edge_growth_cache != NULL)
     {
       do_estimate_edge_time (edge);
-      hints = edge_growth_cache->get (edge)->hints;
+      ipa_hints hints = edge_growth_cache->get (edge)->hints;
       gcc_checking_assert (hints);
       return hints - 1;
     }
@@ -354,8 +360,9 @@  do_estimate_edge_hints (struct cgraph_edge *edge)
   evaluate_properties_for_edge (edge, true, &clause, &nonspec_clause,
 				&avals, true);
   ipa_call_context ctx (callee, clause, nonspec_clause, vNULL, &avals);
-  ctx.estimate_size_and_time (NULL, NULL, NULL, NULL, &hints);
-  hints |= simple_edge_hints (edge);
+  ipa_call_estimates estimates;
+  ctx.estimate_size_and_time (&estimates, false, true);
+  ipa_hints hints = estimates.hints | simple_edge_hints (edge);
   return hints;
 }