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]

Fix C/C++ inlinability tests


Hi,
the reason why current_function_decl is needed in tree_inlinable_function_p is
hackish optimization in C frontend.  C frontend has several reasons for not
inlining C++ frontend miss and the distribution of check seems bit random to me
- for instance we check longjmp and alloca in tree-inline, but setjmp in C
frontend.

The patch moves checks that will become completely frontend independent after
gimplifying into tree-inline (at the present there is one ifdef needed) and
cuts number of walks trought body from three into one.
It also improves warnings on why inlining failed.

OK?
Honza
Tue Sep  9 20:32:37 CEST 2003  Jan Hubicka  <jh@suse.cz>
	* c-objc-common.c (c_cannot_inline_tree_fn): Warn
	on why function is not inlinable; do not check
	the body.
	(inline_forbidden_p): Move to...
	* tree-inline.c (inline_forbidden_p_1): Add warnings;
	deal with alloca, longjmp.
	(inline_forbidden_p): New static function.
	(find_alloca_call_1, find_alloca_call, find_builtin_longjmp_call_1,
	find_builtin_longjmp_call): Kill.
diff -Nrc3p gcc.old3/c-objc-common.c gcc/c-objc-common.c
*** gcc.old3/c-objc-common.c	Tue Sep  9 17:06:55 2003
--- gcc/c-objc-common.c	Tue Sep  9 19:58:29 2003
*************** Software Foundation, 59 Temple Place - S
*** 40,46 ****
  #include "cgraph.h"
  
  static bool c_tree_printer (pretty_printer *, text_info *);
- static tree inline_forbidden_p (tree *, int *, void *);
  static tree start_cdtor (int);
  static void finish_cdtor (tree);
  
--- 40,45 ----
*************** c_disregard_inline_limits (tree fn)
*** 65,174 ****
    return DECL_DECLARED_INLINE_P (fn) && DECL_EXTERNAL (fn);
  }
  
- static tree
- inline_forbidden_p (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED,
- 		    void *fn)
- {
-   tree node = *nodep;
-   tree t;
- 
-   switch (TREE_CODE (node))
-     {
-     case CALL_EXPR:
-       t = get_callee_fndecl (node);
- 
-       if (! t)
- 	break;
- 
-       /* We cannot inline functions that call setjmp.  */
-       if (setjmp_call_p (t))
- 	return node;
- 
-       switch (DECL_FUNCTION_CODE (t))
- 	{
- 	  /* We cannot inline functions that take a variable number of
- 	     arguments.  */
- 	case BUILT_IN_VA_START:
- 	case BUILT_IN_STDARG_START:
- #if 0
- 	  /* Functions that need information about the address of the
-              caller can't (shouldn't?) be inlined.  */
- 	case BUILT_IN_RETURN_ADDRESS:
- #endif
- 	  return node;
- 
- 	default:
- 	  break;
- 	}
- 
-       break;
- 
-     case DECL_STMT:
-       /* We cannot inline functions that contain other functions.  */
-       if (TREE_CODE (TREE_OPERAND (node, 0)) == FUNCTION_DECL
- 	  && DECL_INITIAL (TREE_OPERAND (node, 0)))
- 	return node;
-       break;
- 
-     case GOTO_STMT:
-     case GOTO_EXPR:
-       t = TREE_OPERAND (node, 0);
- 
-       /* We will not inline a function which uses computed goto.  The
- 	 addresses of its local labels, which may be tucked into
- 	 global storage, are of course not constant across
- 	 instantiations, which causes unexpected behavior.  */
-       if (TREE_CODE (t) != LABEL_DECL)
- 	return node;
- 
-       /* We cannot inline a nested function that jumps to a nonlocal
-          label.  */
-       if (TREE_CODE (t) == LABEL_DECL
- 	  && !C_DECL_FILE_SCOPE (t) && DECL_CONTEXT (t) != fn)
- 	return node;
- 
-       break;
- 
-     case RECORD_TYPE:
-     case UNION_TYPE:
-       /* We cannot inline a function of the form
- 
- 	   void F (int i) { struct S { int ar[i]; } s; }
- 
- 	 Attempting to do so produces a catch-22 in tree-inline.c.
- 	 If walk_tree examines the TYPE_FIELDS chain of RECORD_TYPE/
- 	 UNION_TYPE nodes, then it goes into infinite recursion on a
- 	 structure containing a pointer to its own type.  If it doesn't,
- 	 then the type node for S doesn't get adjusted properly when
- 	 F is inlined, and we abort in find_function_data.  */
-       for (t = TYPE_FIELDS (node); t; t = TREE_CHAIN (t))
- 	if (variably_modified_type_p (TREE_TYPE (t)))
- 	  return node;
- 
-     default:
-       break;
-     }
- 
-   return NULL_TREE;
- }
- 
  int
  c_cannot_inline_tree_fn (tree *fnp)
  {
    tree fn = *fnp;
    tree t;
  
    if (flag_really_no_inline
	&& lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn)) == NULL)
!     return 1;
  
    /* Don't auto-inline anything that might not be bound within
       this unit of translation.  */
    if (!DECL_DECLARED_INLINE_P (fn) && !(*targetm.binds_local_p) (fn))
!     goto cannot_inline;
  
    if (! function_attribute_inlinable_p (fn))
!     goto cannot_inline;
  
    /* If a function has pending sizes, we must not defer its
       compilation, and we can't inline it as a tree.  */
--- 64,108 ----
    return DECL_DECLARED_INLINE_P (fn) && DECL_EXTERNAL (fn);
  }
  
  int
  c_cannot_inline_tree_fn (tree *fnp)
  {
    tree fn = *fnp;
    tree t;
+   bool do_warning = (warn_inline
+ 		     && DECL_INLINE (fn)
+ 		     && DECL_DECLARED_INLINE_P (fn)
+ 		     && !DECL_IN_SYSTEM_HEADER (fn));
  
    if (flag_really_no_inline
	&& lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn)) == NULL)
!     {
!       if (do_warning)
! 	warning ("%Hfunction '%F' can never be inlined because it "
! 		 "is supressed using -fno-really-inline",
! 		 &DECL_SOURCE_LOCATION (fn), fn);
!       return 1;
!     }
  
    /* Don't auto-inline anything that might not be bound within
       this unit of translation.  */
    if (!DECL_DECLARED_INLINE_P (fn) && !(*targetm.binds_local_p) (fn))
!     {
!       if (do_warning)
! 	warning ("%Hfunction '%F' can never be inlined because it might not "
! 		 "be bound within this unit of translation",
! 		 &DECL_SOURCE_LOCATION (fn), fn);
!       goto cannot_inline;
!     }
  
    if (! function_attribute_inlinable_p (fn))
!     {
!       if (do_warning)
! 	warning ("%Hfunction '%F' can never be inlined because it use "
! 		 "attributes conflicting with inlining",
! 		 &DECL_SOURCE_LOCATION (fn), fn);
!       goto cannot_inline;
!     }
  
    /* If a function has pending sizes, we must not defer its
       compilation, and we can't inline it as a tree.  */
*************** c_cannot_inline_tree_fn (tree *fnp)
*** 178,184 ****
	put_pending_sizes (t);
  
	if (t)
! 	goto cannot_inline;
      }
  
    if (! C_DECL_FILE_SCOPE (fn))
--- 112,124 ----
	put_pending_sizes (t);
  
	if (t)
! 	{
! 	  if (do_warning)
! 	    warning ("%Hfunction '%F' can never be inlined because it has "
! 		     "pending sizes",
! 		     &DECL_SOURCE_LOCATION (fn), fn);
! 	  goto cannot_inline;
! 	}
      }
  
    if (! C_DECL_FILE_SCOPE (fn))
*************** c_cannot_inline_tree_fn (tree *fnp)
*** 186,216 ****
	/* If a nested function has pending sizes, we may have already
	   saved them.  */
	if (DECL_LANG_SPECIFIC (fn)->pending_sizes)
! 	goto cannot_inline;
!     }
!   else
!     {
!       /* We rely on the fact that this function is called upfront,
! 	 just before we start expanding a function.  If FN is active
! 	 (i.e., it's the current_function_decl or a parent thereof),
! 	 we have to walk FN's saved tree.  Otherwise, we can safely
! 	 assume we have done it before and, if we didn't mark it as
! 	 uninlinable (in which case we wouldn't have been called), it
! 	 is inlinable.  Unfortunately, this strategy doesn't work for
! 	 nested functions, because they're only expanded as part of
! 	 their enclosing functions, so the inlinability test comes in
! 	 late.  */
!       t = current_function_decl;
! 
!       while (t && t != fn)
! 	t = DECL_CONTEXT (t);
!       if (! t)
! 	return 0;
      }
  
-   if (walk_tree (&DECL_SAVED_TREE (fn), inline_forbidden_p, fn, NULL))
-     goto cannot_inline;
- 
    return 0;
  
   cannot_inline:
--- 126,140 ----
	/* If a nested function has pending sizes, we may have already
	   saved them.  */
	if (DECL_LANG_SPECIFIC (fn)->pending_sizes)
! 	{
! 	  if (do_warning)
! 	    warning ("%Hnested function '%F' can never be inlined because it "
! 		     "has possibly saved pending sizes",
! 		     &DECL_SOURCE_LOCATION (fn), fn);
! 	  goto cannot_inline;
! 	}
      }
  
    return 0;
  
   cannot_inline:

diff -Nrc3p gcc.old3/tree-inline.c gcc/tree-inline.c
*** gcc.old3/tree-inline.c	Tue Sep  9 17:06:38 2003
--- gcc/tree-inline.c	Tue Sep  9 20:22:54 2003
*************** static tree initialize_inlined_parameter
*** 128,137 ****
  static void remap_block (tree *, tree, inline_data *);
  static tree add_stmt_to_compound (tree, tree, tree);
  #endif /* INLINER_FOR_JAVA */
- static tree find_alloca_call_1 (tree *, int *, void *);
- static tree find_alloca_call (tree);
- static tree find_builtin_longjmp_call_1 (tree *, int *, void *);
- static tree find_builtin_longjmp_call (tree);
  
  /* Remap DECL during the copying of the BLOCK tree for the function.  */
  
--- 128,133 ----
*************** tree_inlinable_function_p (tree fn)
*** 880,929 ****
    return inlinable_function_p (fn);
  }
  
! /* If *TP is possibly call to alloca, return nonzero.  */
! static tree
! find_alloca_call_1 (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
! 		    void *data ATTRIBUTE_UNUSED)
! {
!   if (alloca_call_p (*tp))
!     return *tp;
!   return NULL;
! }
  
- /* Return subexpression representing possible alloca call, if any.  */
  static tree
! find_alloca_call (tree exp)
  {
!   location_t saved_loc = input_location;
!   tree ret = walk_tree_without_duplicates
! 		(&exp, find_alloca_call_1, NULL);
!   input_location = saved_loc;
!   return ret;
! }
  
! static tree
! find_builtin_longjmp_call_1 (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
! 			     void *data ATTRIBUTE_UNUSED)
! {
!   tree exp = *tp, decl;
  
-   if (TREE_CODE (exp) == CALL_EXPR
-       && TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR
-       && (decl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0),
- 	  TREE_CODE (decl) == FUNCTION_DECL)
-       && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL
-       && DECL_FUNCTION_CODE (decl) == BUILT_IN_LONGJMP)
-     return decl;
  
!   return NULL;
  }
  
  static tree
! find_builtin_longjmp_call (tree exp)
  {
    location_t saved_loc = input_location;
    tree ret = walk_tree_without_duplicates
! 		(&exp, find_builtin_longjmp_call_1, NULL);
    input_location = saved_loc;
    return ret;
  }
--- 876,1022 ----
    return inlinable_function_p (fn);
  }
  
! static const char *inline_forbidden_reason;
  
  static tree
! inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED,
! 		    void *fn)
  {
!   tree node = *nodep;
!   tree t;
  
!   switch (TREE_CODE (node))
!     {
!     case CALL_EXPR:
!       /* Refuse to inline alloca call unless user explicitly forced so as this
! 	 may change program's memory overhead drastically when the function
! 	 using alloca is called in loop.  In GCC present in SPEC2000 inlining
! 	 into schedule_block cause it to require 2GB of ram instead of 256MB.  */
!       if (alloca_call_p (node) && !lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn)))
! 	{
! 	  inline_forbidden_reason = "%Hfunction '%F' can never be inlined "
! 				    "because it use alloca (overwrite using "
! 				    "always_inline attribute)";
! 	  return node;
! 	}
!       t = get_callee_fndecl (node);
!       if (! t)
! 	break;
  
  
!       /* We cannot inline functions that call setjmp.  */
!       if (setjmp_call_p (t))
! 	{
! 	  inline_forbidden_reason = "%Hfunction '%F' can never be inlined"
! 				    " because it use setjmp";
! 	  return node;
! 	}
! 
!       switch (DECL_FUNCTION_CODE (t))
! 	{
! 	  /* We cannot inline functions that take a variable number of
! 	     arguments.  */
! 	case BUILT_IN_VA_START:
! 	case BUILT_IN_STDARG_START:
! 	  {
! 	    inline_forbidden_reason = "%Hfunction '%F' can never be inlined"
! 				      " because it use variable argument lists";
! 	    return node;
! 	  }
! 	case BUILT_IN_LONGJMP:
! 	  {
! 	    /* We can't inline functions that call __builtin_longjmp at all.
! 	       The non-local goto machinery really requires the destination
! 	       be in a different function.  If we allow the function calling
! 	       __builtin_longjmp to be inlined into the function calling
! 	       __builtin_setjmp, Things will Go Awry.  */
! 	    /* ??? Need front end help to identify "regular" non-local goto.  */
!             if (DECL_BUILT_IN_CLASS (t) == BUILT_IN_NORMAL)
! 	      {
! 		inline_forbidden_reason = "%Hfunction '%F' can never be inlined "
! 					  "because it uses setjmp-longjmp "
! 					  "exception handling";
! 	        return node;
! 	      }
! 	  }
! 
! 	default:
! 	  break;
! 	}
!       break;
! 
! #ifndef INLINER_FOR_JAVA
!     case DECL_STMT:
!       /* We cannot inline functions that contain other functions.  */
!       if (TREE_CODE (TREE_OPERAND (node, 0)) == FUNCTION_DECL
! 	  && DECL_INITIAL (TREE_OPERAND (node, 0)))
! 	{
! 	  inline_forbidden_reason = "%Hfunction '%F' can never be inlined "
! 				    "because it contains nested function";
! 	  return node;
! 	}
!       break;
! 
!     case GOTO_STMT:
!     case GOTO_EXPR:
!       t = TREE_OPERAND (node, 0);
! 
!       /* We will not inline a function which uses computed goto.  The
! 	 addresses of its local labels, which may be tucked into
! 	 global storage, are of course not constant across
! 	 instantiations, which causes unexpected behavior.  */
!       if (TREE_CODE (t) != LABEL_DECL)
! 	{
! 	  inline_forbidden_reason = "%Hfunction '%F' can never be inlined "
! 				    "because it contains nonlocal label";
! 	  return node;
! 	}
! 
!       /* We cannot inline a nested function that jumps to a nonlocal
!          label.  */
!       if (TREE_CODE (t) == LABEL_DECL && DECL_CONTEXT (t) != fn)
! 	{
! 	  inline_forbidden_reason = "%Hfunction '%F' can never be inlined "
! 				    " because it contains nonlocal goto";
! 	  return node;
! 	}
! 
!       break;
! 
!     case RECORD_TYPE:
!     case UNION_TYPE:
!       /* We cannot inline a function of the form
! 
! 	   void F (int i) { struct S { int ar[i]; } s; }
! 
! 	 Attempting to do so produces a catch-22 in tree-inline.c.
! 	 If walk_tree examines the TYPE_FIELDS chain of RECORD_TYPE/
! 	 UNION_TYPE nodes, then it goes into infinite recursion on a
! 	 structure containing a pointer to its own type.  If it doesn't,
! 	 then the type node for S doesn't get adjusted properly when
! 	 F is inlined, and we abort in find_function_data.  */
!       for (t = TYPE_FIELDS (node); t; t = TREE_CHAIN (t))
! 	if (variably_modified_type_p (TREE_TYPE (t)))
! 	  {
! 	    inline_forbidden_reason = "%Hfunction '%F' can never be inlined "
! 				      "because it use variable sized variables";
! 	    return node;
! 	  }
! #endif
!     default:
!       break;
!     }
! 
!   return NULL_TREE;
  }
  
+ /* Return subexpression representing possible alloca call, if any.  */
  static tree
! inline_forbidden_p (tree fndecl)
  {
    location_t saved_loc = input_location;
    tree ret = walk_tree_without_duplicates
! 		(&DECL_SAVED_TREE (fndecl), inline_forbidden_p_1, fndecl);
    input_location = saved_loc;
    return ret;
  }
*************** inlinable_function_p (tree fn)
*** 936,942 ****
  {
    bool inlinable = true;
    bool calls_builtin_longjmp = false;
-   bool calls_alloca = false;
  
    /* If we've already decided this function shouldn't be inlined,
       there's no need to check again.  */
--- 1029,1034 ----
*************** inlinable_function_p (tree fn)
*** 980,1003 ****
      inlinable = false;
  #endif /* INLINER_FOR_JAVA */
  
!   /* We can't inline functions that call __builtin_longjmp at all.
!      The non-local goto machinery really requires the destination
!      be in a different function.  If we allow the function calling
!      __builtin_longjmp to be inlined into the function calling
!      __builtin_setjmp, Things will Go Awry.  */
!   /* ??? Need front end help to identify "regular" non-local goto.  */
!   else if (find_builtin_longjmp_call (DECL_SAVED_TREE (fn)))
!     calls_builtin_longjmp = true;
! 
!   /* Refuse to inline alloca call unless user explicitly forced so as this
!      may change program's memory overhead drastically when the function
!      using alloca is called in loop.  In GCC present in SPEC2000 inlining
!      into schedule_block cause it to require 2GB of ram instead of 256MB.  */
!   else if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn)) == NULL
! 	   && find_alloca_call (DECL_SAVED_TREE (fn)))
!     calls_alloca = true;
! 
!   if (calls_builtin_longjmp || calls_alloca)
      {
        /* See if we should warn about uninlinable functions.  Previously,
  	 some of these warnings would be issued while trying to expand
--- 1072,1078 ----
      inlinable = false;
  #endif /* INLINER_FOR_JAVA */
  
!   else if (inline_forbidden_p (fn))
      {
        /* See if we should warn about uninlinable functions.  Previously,
  	 some of these warnings would be issued while trying to expand
*************** inlinable_function_p (tree fn)
*** 1012,1024 ****
  			 && DECL_DECLARED_INLINE_P (fn)
  			 && !DECL_IN_SYSTEM_HEADER (fn));
  
!       if (do_warning && calls_builtin_longjmp)
! 	warning ("%Hfunction '%F' can never be inlined because it uses "
! 		 "setjmp-longjmp exception handling",
! 		 &DECL_SOURCE_LOCATION (fn), fn);
!       if (do_warning && calls_alloca)
! 	warning ("%Hfunction '%F' can never be inlined because it uses "
! 		 "setjmp-longjmp exception handling",
  		 &DECL_SOURCE_LOCATION (fn), fn);
  
        inlinable = false;
--- 1087,1094 ----
  			 && DECL_DECLARED_INLINE_P (fn)
  			 && !DECL_IN_SYSTEM_HEADER (fn));
  
!       if (do_warning)
! 	warning (inline_forbidden_reason,
  		 &DECL_SOURCE_LOCATION (fn), fn);
  
        inlinable = false;


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