c-objc-common.c (c_cannot_inline_tree_fn): Warn on why function is not inlinable; do not check the body.

* 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): ... here; 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.

From-SVN: r71283
This commit is contained in:
Jan Hubicka 2003-09-11 01:45:05 +02:00 committed by Jan Hubicka
parent 25c84396dd
commit f08545a8f2
3 changed files with 187 additions and 181 deletions

View File

@ -1,3 +1,15 @@
Thu Sep 11 01:21:05 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): ... here; 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.
2003-09-10 Richard Henderson <rth@redhat.com>
* cgraph.h (struct cgraph_node): Rename lowered to analyzed.

View File

@ -40,7 +40,6 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
#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);
@ -65,110 +64,45 @@ c_disregard_inline_limits (tree fn)
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
&& !DECL_FILE_SCOPE_P (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;
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)
return 1;
{
if (do_warning)
warning ("%Hfunction '%F' can never be inlined because it "
"is supressed using -fno-inline",
&DECL_SOURCE_LOCATION (fn), fn);
goto cannot_inline;
}
/* 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 (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))
goto cannot_inline;
{
if (do_warning)
warning ("%Hfunction '%F' can never be inlined because it uses "
"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. */
@ -178,7 +112,13 @@ c_cannot_inline_tree_fn (tree *fnp)
put_pending_sizes (t);
if (t)
goto cannot_inline;
{
if (do_warning)
warning ("%Hfunction '%F' can never be inlined because it has "
"pending sizes",
&DECL_SOURCE_LOCATION (fn), fn);
goto cannot_inline;
}
}
if (! DECL_FILE_SCOPE_P (fn))
@ -186,30 +126,14 @@ c_cannot_inline_tree_fn (tree *fnp)
/* If a nested function has pending sizes, we may have already
saved them. */
if (DECL_LANG_SPECIFIC (fn)->pending_sizes)
goto cannot_inline;
{
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;
}
}
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;

View File

@ -128,10 +128,6 @@ static tree initialize_inlined_parameters (inline_data *, tree, tree, tree);
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. */
@ -880,50 +876,148 @@ tree_inlinable_function_p (tree fn)
return inlinable_function_p (fn);
}
/* If *TP is possibly call to alloca, return nonzero. */
static const char *inline_forbidden_reason;
static tree
find_alloca_call_1 (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED,
void *data ATTRIBUTE_UNUSED)
inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED,
void *fn)
{
if (alloca_call_p (*tp))
return *tp;
return NULL;
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 uses alloca (override using "
"the 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 uses 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 uses 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 a 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 a 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 a 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.
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 uses variable sized variables";
return node;
}
#endif
default:
break;
}
return NULL_TREE;
}
/* Return subexpression representing possible alloca call, if any. */
static tree
find_alloca_call (tree exp)
inline_forbidden_p (tree fndecl)
{
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);
(&DECL_SAVED_TREE (fndecl), inline_forbidden_p_1, fndecl);
input_location = saved_loc;
return ret;
}
@ -935,8 +1029,6 @@ static bool
inlinable_function_p (tree fn)
{
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. */
@ -980,24 +1072,7 @@ inlinable_function_p (tree fn)
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)
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
@ -1012,13 +1087,8 @@ inlinable_function_p (tree fn)
&& 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",
if (do_warning)
warning (inline_forbidden_reason,
&DECL_SOURCE_LOCATION (fn), fn);
inlinable = false;