mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-02-24 14:39:39 +08:00
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:
parent
25c84396dd
commit
f08545a8f2
@ -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.
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user