This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH][1/n] Cleanup inlining


This is a first patch in a series to cleanup inlining, focused on
making the code more self-documented (cleanup the predicate confusion)
and eventually reduce the number of --params involved in inlining.

This first patch splits up cgraph_decide_inlining_incrementally with
the goal to make cgraph_early_inlining easy to follow.  The remaining
cgraph_decide_inlining_incrementally is still not obvious with
respect to the cgraph_check_inline_limits and cgraph_default_inline_p
predicates, but that is left to a followup that also includes cleanups
in the IPA inliner.

Bootstrapped and tested on x86_64-unknown-linux-gnu, queued for stage1,
feedback is always appreciated.

2/n will focus on disentangling the various inlining related predicates
into "can" and "want" kinds and unify the "can" kinds in one place
and to compute all function (and not edge) related "can"s in the
place we compute local inline parameters.

Richard.


2011-02-14  Richard Guenther  <rguenther@suse.de>

	* ipa-inline.c (enum inlining_mode): Remove.
	(cgraph_flatten): Use some other token.
	(cgraph_node_inlinable_p): New function, split out from ...
	(cgraph_edge_inlinable_p): New function, split out from ...
	(cgraph_perform_always_inlining): New function, split out from ...
	(cgraph_decide_inlining_incrementally): ... here.
	(cgraph_early_inlining): Re-structure.
	(pass_early_inline): Require SSA form.

Index: gcc/ipa-inline.c
===================================================================
*** gcc/ipa-inline.c.orig	2011-02-14 17:13:59.000000000 +0100
--- gcc/ipa-inline.c	2011-02-14 17:54:06.000000000 +0100
*************** along with GCC; see the file COPYING3.
*** 126,153 ****
  
  #define MAX_TIME 1000000000
  
- /* Mode incremental inliner operate on:
- 
-    In ALWAYS_INLINE only functions marked
-    always_inline are inlined.  This mode is used after detecting cycle during
-    flattening.
- 
-    In SIZE mode, only functions that reduce function body size after inlining
-    are inlined, this is used during early inlining.
- 
-    in ALL mode, everything is inlined.  This is used during flattening.  */
- enum inlining_mode {
-   INLINE_NONE = 0,
-   INLINE_ALWAYS_INLINE,
-   INLINE_SIZE_NORECURSIVE,
-   INLINE_SIZE,
-   INLINE_ALL
- };
- 
- static bool
- cgraph_decide_inlining_incrementally (struct cgraph_node *, enum inlining_mode);
- static void cgraph_flatten (struct cgraph_node *node);
- 
  
  /* Statistics we collect about inlining algorithm.  */
  static int ncalls_inlined;
--- 126,131 ----
*************** cgraph_flatten (struct cgraph_node *node
*** 1315,1321 ****
    /* We shouldn't be called recursively when we are being processed.  */
    gcc_assert (node->aux == NULL);
  
!   node->aux = (void *)(size_t) INLINE_ALL;
  
    for (e = node->callees; e; e = e->next_callee)
      {
--- 1293,1299 ----
    /* We shouldn't be called recursively when we are being processed.  */
    gcc_assert (node->aux == NULL);
  
!   node->aux = (void *) node;
  
    for (e = node->callees; e; e = e->next_callee)
      {
*************** cgraph_flatten (struct cgraph_node *node
*** 1389,1395 ****
        orig_callee = e->callee;
        cgraph_mark_inline_edge (e, true, NULL);
        if (e->callee != orig_callee)
! 	orig_callee->aux = (void *)(size_t) INLINE_ALL;
        cgraph_flatten (e->callee);
        if (e->callee != orig_callee)
  	orig_callee->aux = NULL;
--- 1367,1373 ----
        orig_callee = e->callee;
        cgraph_mark_inline_edge (e, true, NULL);
        if (e->callee != orig_callee)
! 	orig_callee->aux = (void *) node;
        cgraph_flatten (e->callee);
        if (e->callee != orig_callee)
  	orig_callee->aux = NULL;
*************** leaf_node_p (struct cgraph_node *n)
*** 1564,1728 ****
    return true;
  }
  
  /* Decide on the inlining.  We do so in the topological order to avoid
     expenses on updating data structures.  */
  
  static bool
! cgraph_decide_inlining_incrementally (struct cgraph_node *node,
! 				      enum inlining_mode mode)
  {
    struct cgraph_edge *e;
    bool inlined = false;
    cgraph_inline_failed_t failed_reason;
  
! #ifdef ENABLE_CHECKING
!   verify_cgraph_node (node);
! #endif
  
!   if (mode != INLINE_ALWAYS_INLINE && mode != INLINE_SIZE_NORECURSIVE
!       && lookup_attribute ("flatten", DECL_ATTRIBUTES (node->decl)) != NULL)
      {
        if (dump_file)
! 	fprintf (dump_file, "Incrementally flattening %s\n",
! 		 cgraph_node_name (node));
!       mode = INLINE_ALL;
!     }
  
!   /* First of all look for always inline functions.  */
!   if (mode != INLINE_SIZE_NORECURSIVE)
!     for (e = node->callees; e; e = e->next_callee)
!       {
! 	if (!e->callee->local.disregard_inline_limits
! 	    && (mode != INLINE_ALL || !e->callee->local.inlinable))
  	  continue;
! 	if (dump_file)
! 	  fprintf (dump_file,
! 		   "Considering to always inline inline candidate %s.\n",
! 		   cgraph_node_name (e->callee));
! 	if (cgraph_recursive_inlining_p (node, e->callee, &e->inline_failed))
! 	  {
! 	    if (dump_file)
! 	      fprintf (dump_file, "Not inlining: recursive call.\n");
! 	    continue;
! 	  }
! 	if (!tree_can_inline_p (e)
! 	    || e->call_stmt_cannot_inline_p)
! 	  {
! 	    if (dump_file)
! 	      fprintf (dump_file,
! 		       "Not inlining: %s",
! 		       cgraph_inline_failed_string (e->inline_failed));
! 	    continue;
! 	  }
! 	if (gimple_in_ssa_p (DECL_STRUCT_FUNCTION (node->decl))
! 	    != gimple_in_ssa_p (DECL_STRUCT_FUNCTION (e->callee->decl)))
! 	  {
! 	    if (dump_file)
! 	      fprintf (dump_file, "Not inlining: SSA form does not match.\n");
! 	    continue;
! 	  }
! 	if (!e->callee->analyzed)
! 	  {
! 	    if (dump_file)
! 	      fprintf (dump_file,
! 		       "Not inlining: Function body no longer available.\n");
! 	    continue;
! 	  }
  
! 	if (dump_file)
! 	  fprintf (dump_file, " Inlining %s into %s.\n",
! 		   cgraph_node_name (e->callee),
! 		   cgraph_node_name (e->caller));
! 	cgraph_mark_inline_edge (e, true, NULL);
! 	inlined = true;
!       }
  
!   /* Now do the automatic inlining.  */
!   if (mode != INLINE_ALL && mode != INLINE_ALWAYS_INLINE
!       /* Never inline regular functions into always-inline functions
! 	 during incremental inlining.  */
!       && !node->local.disregard_inline_limits)
!     {
!       for (e = node->callees; e; e = e->next_callee)
! 	{
! 	  int allowed_growth = 0;
! 	  if (!e->callee->local.inlinable
! 	      || !e->inline_failed
! 	      || e->callee->local.disregard_inline_limits)
! 	    continue;
  	  if (dump_file)
! 	    fprintf (dump_file, "Considering inline candidate %s.\n",
! 		     cgraph_node_name (e->callee));
! 	  if (cgraph_recursive_inlining_p (node, e->callee, &e->inline_failed))
! 	    {
! 	      if (dump_file)
! 		fprintf (dump_file, "Not inlining: recursive call.\n");
! 	      continue;
! 	    }
! 	  if (gimple_in_ssa_p (DECL_STRUCT_FUNCTION (node->decl))
! 	      != gimple_in_ssa_p (DECL_STRUCT_FUNCTION (e->callee->decl)))
! 	    {
! 	      if (dump_file)
! 		fprintf (dump_file,
! 			 "Not inlining: SSA form does not match.\n");
! 	      continue;
! 	    }
! 
! 	  if (cgraph_maybe_hot_edge_p (e) && leaf_node_p (e->callee)
! 	      && optimize_function_for_speed_p (cfun))
! 	    allowed_growth = PARAM_VALUE (PARAM_EARLY_INLINING_INSNS);
! 
! 	  /* When the function body would grow and inlining the function
! 	     won't eliminate the need for offline copy of the function,
! 	     don't inline.  */
! 	  if (((mode == INLINE_SIZE || mode == INLINE_SIZE_NORECURSIVE)
! 	       || (!flag_inline_functions
! 		   && !DECL_DECLARED_INLINE_P (e->callee->decl)))
! 	      && (cgraph_estimate_size_after_inlining (e->caller, e->callee)
! 		  > e->caller->global.size + allowed_growth)
! 	      && cgraph_estimate_growth (e->callee) > allowed_growth)
! 	    {
! 	      if (dump_file)
! 		fprintf (dump_file,
! 			 "Not inlining: code size would grow by %i.\n",
! 			 cgraph_estimate_size_after_inlining (e->caller,
! 							      e->callee)
! 			 - e->caller->global.size);
! 	      continue;
! 	    }
! 	  if (e->call_stmt_cannot_inline_p
! 	      || !tree_can_inline_p (e))
! 	    {
! 	      if (dump_file)
! 		fprintf (dump_file,
! 			 "Not inlining: call site not inlinable.\n");
! 	      continue;
! 	    }
! 	  if (!e->callee->analyzed)
! 	    {
! 	      if (dump_file)
! 		fprintf (dump_file,
! 			 "Not inlining: Function body no longer available.\n");
! 	      continue;
! 	    }
! 	  if (!cgraph_check_inline_limits (node, e->callee, &e->inline_failed))
! 	    {
! 	      if (dump_file)
! 		fprintf (dump_file, "Not inlining: %s.\n",
! 			 cgraph_inline_failed_string (e->inline_failed));
! 	      continue;
! 	    }
! 	  if (cgraph_default_inline_p (e->callee, &failed_reason))
! 	    {
! 	      if (dump_file)
! 		fprintf (dump_file, " Inlining %s into %s.\n",
! 			 cgraph_node_name (e->callee),
! 			 cgraph_node_name (e->caller));
! 	      cgraph_mark_inline_edge (e, true, NULL);
! 	      inlined = true;
! 	    }
  	}
      }
    return inlined;
  }
  
--- 1542,1719 ----
    return true;
  }
  
+ /* Return true if we can inline the function NODE.  Dump information
+    to FILE if non-NULL.  */
+ 
+ static bool
+ cgraph_node_inlinable_p (struct cgraph_node *node, FILE *file)
+ {
+   if (!node->local.inlinable)
+     {
+       if (file)
+ 	fprintf (file, "Not inlining: Function not inlinable.\n");
+       return false;
+     }
+   if (!node->analyzed)
+     {
+       if (file)
+ 	fprintf (file, "Not inlining: Function body not available.\n");
+       return false;
+     }
+   return true;
+ }
+ 
+ /* Return true if we can inline the edge E.  Dump information to FILE
+    if non-NULL.  */
+ 
+ static bool
+ cgraph_edge_inlinable_p (struct cgraph_edge *e, FILE *file)
+ {
+   if (!cgraph_node_inlinable_p (e->callee, file))
+     return false;
+   if (!tree_can_inline_p (e)
+       || e->call_stmt_cannot_inline_p)
+     {
+       if (file)
+ 	fprintf (file, "Not inlining: %s.\n",
+ 		 cgraph_inline_failed_string (e->inline_failed));
+       return false;
+     }
+   if (!gimple_in_ssa_p (DECL_STRUCT_FUNCTION (e->caller->decl))
+       || !gimple_in_ssa_p (DECL_STRUCT_FUNCTION (e->callee->decl)))
+     {
+       if (file)
+ 	fprintf (file, "Not inlining: not in SSA form.\n");
+       return false;
+     }
+   return true;
+ }
+ 
+ /* Inlining always-inline function calls in NODE.  */
+ 
+ static bool
+ cgraph_perform_always_inlining (struct cgraph_node *node)
+ {
+   struct cgraph_edge *e;
+   bool inlined = false;
+ 
+   for (e = node->callees; e; e = e->next_callee)
+     {
+       if (!e->callee->local.disregard_inline_limits)
+ 	continue;
+ 
+       if (dump_file)
+ 	fprintf (dump_file,
+ 		 "Considering always-inline candidate %s.\n",
+ 		 cgraph_node_name (e->callee));
+ 
+       /* Recursion of always-inline functions isn't sensible.  */
+       if (node->decl == e->callee->decl)
+ 	{
+ 	  if (dump_file)
+ 	    fprintf (dump_file, "Not inlining: recursive call.\n");
+ 	  e->inline_failed = CIF_RECURSIVE_INLINING;
+ 	  continue;
+ 	}
+ 
+       if (!cgraph_edge_inlinable_p (e, dump_file))
+ 	continue;
+ 
+       if (dump_file)
+ 	fprintf (dump_file, " Inlining %s into %s.\n",
+ 		 cgraph_node_name (e->callee),
+ 		 cgraph_node_name (e->caller));
+       cgraph_mark_inline_edge (e, true, NULL);
+       inlined = true;
+     }
+ 
+   return inlined;
+ }
+ 
+ 
  /* Decide on the inlining.  We do so in the topological order to avoid
     expenses on updating data structures.  */
  
  static bool
! cgraph_decide_inlining_incrementally (struct cgraph_node *node)
  {
    struct cgraph_edge *e;
    bool inlined = false;
    cgraph_inline_failed_t failed_reason;
  
!   /* Never inline regular functions into always-inline functions
!      during incremental inlining.  */
!   if (node->local.disregard_inline_limits)
!     return false;
  
!   for (e = node->callees; e; e = e->next_callee)
      {
+       int allowed_growth = 0;
+ 
+       if (!e->callee->local.inlinable
+ 	  || !e->inline_failed
+ 	  || e->callee->local.disregard_inline_limits)
+ 	continue;
+ 
+       /* Do not consider functions not declared inline.  */
+       if (!DECL_DECLARED_INLINE_P (e->callee->decl)
+ 	  && !flag_inline_small_functions
+ 	  && !flag_inline_functions)
+ 	continue;
+ 
        if (dump_file)
! 	fprintf (dump_file, "Considering inline candidate %s.\n",
! 		 cgraph_node_name (e->callee));
  
!       if (node->decl == e->callee->decl)
! 	{
! 	  if (dump_file)
! 	    fprintf (dump_file, "Not inlining: recursive call.\n");
  	  continue;
! 	}
  
!       if (!cgraph_edge_inlinable_p (e, dump_file))
! 	continue;
  
!       if (cgraph_maybe_hot_edge_p (e) && leaf_node_p (e->callee)
! 	  && optimize_function_for_speed_p (cfun))
! 	allowed_growth = PARAM_VALUE (PARAM_EARLY_INLINING_INSNS);
! 
!       /* When the function body would grow and inlining the function
! 	 won't eliminate the need for offline copy of the function,
! 	 don't inline.  */
!       if ((!flag_inline_functions
! 	   && !DECL_DECLARED_INLINE_P (e->callee->decl))
! 	  && (cgraph_estimate_size_after_inlining (e->caller, e->callee)
! 	      > e->caller->global.size + allowed_growth)
! 	  && cgraph_estimate_growth (e->callee) > allowed_growth)
! 	{
  	  if (dump_file)
! 	    fprintf (dump_file,
! 		     "Not inlining: code size would grow by %i.\n",
! 		     cgraph_estimate_size_after_inlining (e->caller,
! 							  e->callee)
! 		     - e->caller->global.size);
! 	  continue;
! 	}
!       if (!cgraph_check_inline_limits (node, e->callee, &e->inline_failed))
! 	{
! 	  if (dump_file)
! 	    fprintf (dump_file, "Not inlining: %s.\n",
! 		     cgraph_inline_failed_string (e->inline_failed));
! 	  continue;
! 	}
!       if (cgraph_default_inline_p (e->callee, &failed_reason))
! 	{
! 	  if (dump_file)
! 	    fprintf (dump_file, " Inlining %s into %s.\n",
! 		     cgraph_node_name (e->callee),
! 		     cgraph_node_name (e->caller));
! 	  cgraph_mark_inline_edge (e, true, NULL);
! 	  inlined = true;
  	}
      }
+ 
    return inlined;
  }
  
*************** cgraph_early_inlining (void)
*** 1741,1791 ****
    struct cgraph_node *node = cgraph_node (current_function_decl);
    unsigned int todo = 0;
    int iterations = 0;
  
    if (seen_error ())
      return 0;
  
    if (!optimize
        || flag_no_inline
        || !flag_early_inlining)
      {
!       /* When not optimizing or not inlining inline only always-inline
! 	 functions.  */
!       cgraph_decide_inlining_incrementally (node, INLINE_ALWAYS_INLINE);
!       timevar_push (TV_INTEGRATION);
!       todo |= optimize_inline_calls (current_function_decl);
!       timevar_pop (TV_INTEGRATION);
      }
    else
      {
-       if (lookup_attribute ("flatten",
- 			    DECL_ATTRIBUTES (node->decl)) != NULL)
- 	{
- 	  if (dump_file)
- 	    fprintf (dump_file,
- 		     "Flattening %s\n", cgraph_node_name (node));
- 	  cgraph_flatten (node);
- 	  timevar_push (TV_INTEGRATION);
- 	  todo |= optimize_inline_calls (current_function_decl);
- 	  timevar_pop (TV_INTEGRATION);
- 	}
        /* We iterate incremental inlining to get trivial cases of indirect
  	 inlining.  */
        while (iterations < PARAM_VALUE (PARAM_EARLY_INLINER_MAX_ITERATIONS)
! 	     && cgraph_decide_inlining_incrementally (node,
! 						      iterations
! 						      ? INLINE_SIZE_NORECURSIVE
! 						      : INLINE_SIZE))
  	{
  	  timevar_push (TV_INTEGRATION);
  	  todo |= optimize_inline_calls (current_function_decl);
- 	  iterations++;
  	  timevar_pop (TV_INTEGRATION);
  	}
        if (dump_file)
  	fprintf (dump_file, "Iterations: %i\n", iterations);
      }
  
    cfun->always_inline_functions_inlined = true;
  
    return todo;
--- 1732,1789 ----
    struct cgraph_node *node = cgraph_node (current_function_decl);
    unsigned int todo = 0;
    int iterations = 0;
+   bool inlined = false;
  
    if (seen_error ())
      return 0;
  
+ #ifdef ENABLE_CHECKING
+   verify_cgraph_node (node);
+ #endif
+ 
+   /* Even when not optimizing or not inlining inline always-inline
+      functions.  */
+   inlined = cgraph_perform_always_inlining (node);
+ 
    if (!optimize
        || flag_no_inline
        || !flag_early_inlining)
+     ;
+   else if (lookup_attribute ("flatten",
+ 			     DECL_ATTRIBUTES (node->decl)) != NULL)
      {
!       /* When the function is marked to be flattened, recursively inline
! 	 all calls in it.  */
!       if (dump_file)
! 	fprintf (dump_file,
! 		 "Flattening %s\n", cgraph_node_name (node));
!       cgraph_flatten (node);
!       inlined = true;
      }
    else
      {
        /* We iterate incremental inlining to get trivial cases of indirect
  	 inlining.  */
        while (iterations < PARAM_VALUE (PARAM_EARLY_INLINER_MAX_ITERATIONS)
! 	     && cgraph_decide_inlining_incrementally (node))
  	{
  	  timevar_push (TV_INTEGRATION);
  	  todo |= optimize_inline_calls (current_function_decl);
  	  timevar_pop (TV_INTEGRATION);
+ 	  iterations++;
+ 	  inlined = false;
  	}
        if (dump_file)
  	fprintf (dump_file, "Iterations: %i\n", iterations);
      }
  
+   if (inlined)
+     {
+       timevar_push (TV_INTEGRATION);
+       todo |= optimize_inline_calls (current_function_decl);
+       timevar_pop (TV_INTEGRATION);
+     }
+ 
    cfun->always_inline_functions_inlined = true;
  
    return todo;
*************** struct gimple_opt_pass pass_early_inline
*** 1802,1808 ****
    NULL,					/* next */
    0,					/* static_pass_number */
    TV_INLINE_HEURISTICS,			/* tv_id */
!   0,	                                /* properties_required */
    0,					/* properties_provided */
    0,					/* properties_destroyed */
    0,					/* todo_flags_start */
--- 1800,1806 ----
    NULL,					/* next */
    0,					/* static_pass_number */
    TV_INLINE_HEURISTICS,			/* tv_id */
!   PROP_ssa,                             /* properties_required */
    0,					/* properties_provided */
    0,					/* properties_destroyed */
    0,					/* todo_flags_start */


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]