Commit 4838c5ee by Alexandre Oliva Committed by Alexandre Oliva

Makefile.in (c-decl.o): Depend on tree-inline.h.

* Makefile.in (c-decl.o): Depend on tree-inline.h.
(c-lang.o): Likewise, as well as insn-config.h and integrate.h.
* c-decl.c: Include tree-inline.h.
(c_expand_body): Call optimize_inline_calls. Determine whether
a function is inlinable upfront, and only clear
DECL_SAVED_TREE, DECL_INITIAL and DECL_ARGUMENTS if it isn't.
* c-lang.c: Include tree-inline.h, insn-config.h and integrate.h.
(c_disregard_inline_limits): New function.
(inline_forbidden_p, c_cannot_inline_tree_fn): Likewise.
(c_post_options): Enable tree inlining if inlining is enabled.
Don't inline trees when instrumenting functions.
(c_init): Initialize lang_disregard_inline_limits and
lang_cannot_inline_tree_fn.
* tree-inline.c (initialize_inlined_parameters): Handle calls
with fewer arguments than declared parameters, and fewer
parameters than passed arguments.  Don't assume value is a
DECL.
(declare_return_variable): Convert return value back to the
original type, if it was promoted.
(tree_inlinable_function_p): New function.
(inlinable_function_p): Don't look at DECL_INLINE if we're
inlining all functions.  Make it work with a NULL id.
Re-check DECL_UNINLINABLE after language-specific checks.
(varargs_function_p): Move back to cp/tree.c.
* tree-inline.h (tree_inlinable_function_p): Declare it.
(varargs_function_p): Removed declaration.
* integrate.h (function_attribute_inlinable_p): Declare it.
* integrate.c (function_attribute_inlinable_p): Export it.
(save_for_inline): Don't bother to prepare argvec when not
inlining.
* cse.c (check_for_label_ref): Don't check deleted labels.

From-SVN: r46025
parent 8d5463d4
2001-10-05 Alexandre Oliva <aoliva@redhat.com>
* Makefile.in (c-decl.o): Depend on tree-inline.h.
(c-lang.o): Likewise, as well as insn-config.h and integrate.h.
* c-decl.c: Include tree-inline.h.
(c_expand_body): Call optimize_inline_calls. Determine whether
a function is inlinable upfront, and only clear
DECL_SAVED_TREE, DECL_INITIAL and DECL_ARGUMENTS if it isn't.
* c-lang.c: Include tree-inline.h, insn-config.h and integrate.h.
(c_disregard_inline_limits): New function.
(inline_forbidden_p, c_cannot_inline_tree_fn): Likewise.
(c_post_options): Enable tree inlining if inlining is enabled.
Don't inline trees when instrumenting functions.
(c_init): Initialize lang_disregard_inline_limits and
lang_cannot_inline_tree_fn.
* tree-inline.c (initialize_inlined_parameters): Handle calls
with fewer arguments than declared parameters, and fewer
parameters than passed arguments. Don't assume value is a
DECL.
(declare_return_variable): Convert return value back to the
original type, if it was promoted.
(tree_inlinable_function_p): New function.
(inlinable_function_p): Don't look at DECL_INLINE if we're
inlining all functions. Make it work with a NULL id.
Re-check DECL_UNINLINABLE after language-specific checks.
(varargs_function_p): Move back to cp/tree.c.
* tree-inline.h (tree_inlinable_function_p): Declare it.
(varargs_function_p): Removed declaration.
* integrate.h (function_attribute_inlinable_p): Declare it.
* integrate.c (function_attribute_inlinable_p): Export it.
(save_for_inline): Don't bother to prepare argvec when not
inlining.
* cse.c (check_for_label_ref): Don't check deleted labels.
* Makefile.in (tree-inline.o): Depend on newly-included headers.
* tree-inline.c: Include headers needed for the functions moved in.
(struct inline_data, INSNS_PER_STMT): Moved from cp/optimize.c.
......
......@@ -1168,12 +1168,12 @@ $(srcdir)/c-parse.y: c-parse.in
c-decl.o : c-decl.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(RTL_H) $(C_TREE_H) \
$(GGC_H) $(TARGET_H) c-lex.h flags.h function.h output.h $(EXPR_H) \
debug.h toplev.h intl.h $(TM_P_H)
debug.h toplev.h intl.h $(TM_P_H) tree-inline.h
c-typeck.o : c-typeck.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(C_TREE_H) \
$(TARGET_H) flags.h intl.h output.h $(EXPR_H) $(RTL_H) toplev.h $(TM_P_H)
c-lang.o : c-lang.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(C_TREE_H) \
$(GGC_H) c-lex.h toplev.h diagnostic.h output.h function.h \
$(RTL_H) $(EXPR_H)
$(RTL_H) $(EXPR_H) tree-inline.h insn-config.h integrate.h
c-lex.o : c-lex.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) $(RTL_H) c-lex.h \
debug.h $(C_TREE_H) \
c-pragma.h input.h intl.h flags.h toplev.h output.h \
......
......@@ -30,6 +30,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
#include "system.h"
#include "intl.h"
#include "tree.h"
#include "tree-inline.h"
#include "rtl.h"
#include "flags.h"
#include "function.h"
......@@ -6707,11 +6708,24 @@ c_expand_body (fndecl, nested_p)
tree fndecl;
int nested_p;
{
int uninlinable = 1;
/* There's no reason to do any of the work here if we're only doing
semantic analysis; this code just generates RTL. */
if (flag_syntax_only)
return;
if (flag_inline_trees)
{
/* First, cache whether the current function is inlinable. Some
predicates depend on cfun and current_function_decl to
function completely. */
uninlinable = ! tree_inlinable_function_p (fndecl);
/* Then, inline any functions called in it. */
optimize_inline_calls (fndecl);
}
if (nested_p)
{
/* Make sure that we will evaluate variable-sized types involved
......@@ -6751,8 +6765,11 @@ c_expand_body (fndecl, nested_p)
/* Generate the RTL for this function. */
expand_stmt (DECL_SAVED_TREE (fndecl));
/* Allow the body of the function to be garbage collected. */
DECL_SAVED_TREE (fndecl) = NULL_TREE;
if (uninlinable)
{
/* Allow the body of the function to be garbage collected. */
DECL_SAVED_TREE (fndecl) = NULL_TREE;
}
/* We hard-wired immediate_size_expand to zero above.
expand_function_end will decrement this variable. So, we set the
......@@ -6813,7 +6830,8 @@ c_expand_body (fndecl, nested_p)
}
}
if (DECL_SAVED_INSNS (fndecl) == 0 && ! nested_p)
if (DECL_SAVED_INSNS (fndecl) == 0 && ! nested_p
&& ! flag_inline_trees)
{
/* Stop pointing to the local nodes about to be freed.
But DECL_INITIAL must remain nonzero so we know this
......
......@@ -23,6 +23,7 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
#include "config.h"
#include "system.h"
#include "tree.h"
#include "tree-inline.h"
#include "function.h"
#include "input.h"
#include "toplev.h"
......@@ -35,12 +36,16 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
#include "c-tree.h"
#include "c-lex.h"
#include "cpplib.h"
#include "insn-config.h"
#include "integrate.h"
static int c_tree_printer PARAMS ((output_buffer *));
static int c_missing_noreturn_ok_p PARAMS ((tree));
static void c_init PARAMS ((void));
static void c_init_options PARAMS ((void));
static void c_post_options PARAMS ((void));
static int c_disregard_inline_limits PARAMS ((tree));
static int c_cannot_inline_tree_fn PARAMS ((tree *));
/* Each front end provides its own. */
struct lang_hooks lang_hooks = {c_init,
......@@ -54,6 +59,22 @@ static void
c_post_options ()
{
cpp_post_options (parse_in);
/* Use tree inlining if possible. Function instrumentation is only
done in the RTL level, so we disable tree inlining. */
if (! flag_instrument_function_entry_exit)
{
if (!flag_no_inline)
{
flag_inline_trees = 1;
flag_no_inline = 1;
}
if (flag_inline_functions)
{
flag_inline_trees = 2;
flag_inline_functions = 0;
}
}
}
static void
......@@ -91,6 +112,8 @@ c_init ()
diagnostic_format_decoder (global_dc) = &c_tree_printer;
lang_expand_decl_stmt = &c_expand_decl_stmt;
lang_missing_noreturn_ok_p = &c_missing_noreturn_ok_p;
lang_disregard_inline_limits = &c_disregard_inline_limits;
lang_cannot_inline_tree_fn = &c_cannot_inline_tree_fn;
c_parse_init ();
}
......@@ -308,3 +331,155 @@ c_missing_noreturn_ok_p (decl)
ok for the `main' function in hosted implementations. */
return flag_hosted && MAIN_NAME_P (DECL_ASSEMBLER_NAME (decl));
}
/* We want to inline `extern inline' functions even if this would
violate inlining limits. Some glibc and linux constructs depend on
such functions always being inlined when optimizing. */
static int
c_disregard_inline_limits (fn)
tree fn;
{
return DECL_INLINE (fn) && DECL_EXTERNAL (fn);
}
static tree inline_forbidden_p PARAMS ((tree *, int *, void *));
static tree
inline_forbidden_p (nodep, walk_subtrees, fn)
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_VARARGS_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 behaviour. */
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_CONTEXT (t) && DECL_CONTEXT (t) != fn)
return node;
break;
default:
break;
}
return NULL_TREE;
}
static int
c_cannot_inline_tree_fn (fnp)
tree *fnp;
{
tree fn = *fnp;
tree t;
if (! function_attribute_inlinable_p (fn))
{
DECL_UNINLINABLE (fn) = 1;
return 1;
}
/* If a function has pending sizes, we must not defer its
compilation, and we can't inline it as a tree. */
if (fn == current_function_decl)
{
t = get_pending_sizes ();
put_pending_sizes (t);
if (t)
{
DECL_UNINLINABLE (fn) = 1;
return 1;
}
}
if (DECL_CONTEXT (fn))
{
/* If a nested function has pending sizes, we may have already
saved them. */
if (DECL_LANG_SPECIFIC (fn)->pending_sizes)
{
DECL_UNINLINABLE (fn) = 1;
return 1;
}
}
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))
{
DECL_UNINLINABLE (fn) = 1;
return 1;
}
return 0;
}
......@@ -7376,6 +7376,7 @@ check_for_label_ref (rtl, data)
LABEL_REF for a CODE_LABEL that isn't in the insn chain, don't do this
since no REG_LABEL will be added. */
return (GET_CODE (*rtl) == LABEL_REF
&& LABEL_P (XEXP (*rtl, 0))
&& INSN_UID (XEXP (*rtl, 0)) != 0
&& ! find_reg_note (insn, REG_LABEL, XEXP (*rtl, 0)));
}
......
......@@ -440,8 +440,8 @@ save_for_inline (fndecl)
Also set up ARG_VECTOR, which holds the unmodified DECL_RTX values
for the parms, prior to elimination of virtual registers.
These values are needed for substituting parms properly. */
parmdecl_map = (tree *) xmalloc (max_parm_reg * sizeof (tree));
if (! flag_no_inline)
parmdecl_map = (tree *) xmalloc (max_parm_reg * sizeof (tree));
/* Make and emit a return-label if we have not already done so. */
......@@ -451,7 +451,10 @@ save_for_inline (fndecl)
emit_label (return_label);
}
argvec = initialize_for_inline (fndecl);
if (! flag_no_inline)
argvec = initialize_for_inline (fndecl);
else
argvec = NULL;
/* Delete basic block notes created by early run of find_basic_block.
The notes would be later used by find_basic_blocks to reuse the memory
......@@ -468,27 +471,31 @@ save_for_inline (fndecl)
if (GET_CODE (insn) != NOTE)
abort ();
/* Get the insn which signals the end of parameter setup code. */
first_nonparm_insn = get_first_nonparm_insn ();
/* Now just scan the chain of insns to see what happens to our
PARM_DECLs. If a PARM_DECL is used but never modified, we
can substitute its rtl directly when expanding inline (and
perform constant folding when its incoming value is constant).
Otherwise, we have to copy its value into a new register and track
the new register's life. */
in_nonparm_insns = 0;
save_parm_insns (insn, first_nonparm_insn);
cfun->inl_max_label_num = max_label_num ();
cfun->inl_last_parm_insn = cfun->x_last_parm_insn;
cfun->original_arg_vector = argvec;
if (! flag_no_inline)
{
/* Get the insn which signals the end of parameter setup code. */
first_nonparm_insn = get_first_nonparm_insn ();
/* Now just scan the chain of insns to see what happens to our
PARM_DECLs. If a PARM_DECL is used but never modified, we
can substitute its rtl directly when expanding inline (and
perform constant folding when its incoming value is
constant). Otherwise, we have to copy its value into a new
register and track the new register's life. */
in_nonparm_insns = 0;
save_parm_insns (insn, first_nonparm_insn);
cfun->inl_max_label_num = max_label_num ();
cfun->inl_last_parm_insn = cfun->x_last_parm_insn;
cfun->original_arg_vector = argvec;
}
cfun->original_decl_initial = DECL_INITIAL (fndecl);
cfun->no_debugging_symbols = (write_symbols == NO_DEBUG);
DECL_SAVED_INSNS (fndecl) = cfun;
/* Clean up. */
free (parmdecl_map);
if (! flag_no_inline)
free (parmdecl_map);
}
/* Scan the chain of insns to see what happens to our PARM_DECLs. If a
......
......@@ -483,23 +483,25 @@ initialize_inlined_parameters (id, args, fn)
/* Loop through the parameter declarations, replacing each with an
equivalent VAR_DECL, appropriately initialized. */
for (p = parms, a = args; p; a = TREE_CHAIN (a), p = TREE_CHAIN (p))
for (p = parms, a = args; p;
a = a ? TREE_CHAIN (a) : a, p = TREE_CHAIN (p))
{
tree init_stmt;
tree var;
tree value;
/* Find the initializer. */
value = TREE_VALUE (a);
value = a ? TREE_VALUE (a) : NULL_TREE;
/* If the parameter is never assigned to, we may not need to
create a new variable here at all. Instead, we may be able
to just use the argument value. */
if (TREE_READONLY (p)
&& !TREE_ADDRESSABLE (p)
&& !TREE_SIDE_EFFECTS (value))
&& value && !TREE_SIDE_EFFECTS (value))
{
/* Simplify the value, if possible. */
value = fold (decl_constant_value (value));
value = fold (DECL_P (value) ? decl_constant_value (value) : value);
/* We can't risk substituting complex expressions. They
might contain variables that will be assigned to later.
......@@ -567,6 +569,23 @@ initialize_inlined_parameters (id, args, fn)
}
}
/* Evaluate trailing arguments. */
for (; a; a = TREE_CHAIN (a))
{
tree init_stmt;
tree value;
/* Find the initializer. */
value = a ? TREE_VALUE (a) : NULL_TREE;
if (! value || ! TREE_SIDE_EFFECTS (value))
continue;
init_stmt = build_stmt (EXPR_STMT, value);
TREE_CHAIN (init_stmt) = init_stmts;
init_stmts = init_stmt;
}
/* The initialization statements have been built up in reverse
order. Straighten them out now. */
return nreverse (init_stmts);
......@@ -606,9 +625,15 @@ declare_return_variable (id, use_stmt)
(splay_tree_key) result,
(splay_tree_value) var);
/* Build the USE_STMT. */
*use_stmt = build_stmt (EXPR_STMT, var);
/* Build the USE_STMT. If the return type of the function was
promoted, convert it back to the expected type. */
if (TREE_TYPE (var) == TREE_TYPE (TREE_TYPE (fn)))
*use_stmt = build_stmt (EXPR_STMT, var);
else
*use_stmt = build_stmt (EXPR_STMT,
build1 (NOP_EXPR, TREE_TYPE (TREE_TYPE (fn)),
var));
/* Build the declaration statement if FN does not return an
aggregate. */
if (need_return_decl)
......@@ -619,7 +644,18 @@ declare_return_variable (id, use_stmt)
return NULL_TREE;
}
/* Returns non-zero if FN is a function that can be inlined. */
/* Returns non-zero if a function can be inlined as a tree. */
int
tree_inlinable_function_p (fn)
tree fn;
{
return inlinable_function_p (fn, NULL);
}
/* Returns non-zero if FN is a function that can be inlined into the
inlining context ID_. If ID_ is NULL, check whether the function
can be inlined at all. */
static int
inlinable_function_p (fn, id)
......@@ -637,11 +673,11 @@ inlinable_function_p (fn, id)
inlinable = 0;
/* If we're not inlining things, then nothing is inlinable. */
if (!flag_inline_trees)
if (! flag_inline_trees)
;
/* If the function was not declared `inline', then we don't inline
it. */
else if (!DECL_INLINE (fn))
/* If we're not inlining all functions and the function was not
declared `inline', we don't inline it. */
else if (flag_inline_trees < 2 && ! DECL_INLINE (fn))
;
/* We can't inline functions that are too big. Only allow a single
function to eat up half of our budget. Make special allowance
......@@ -657,14 +693,14 @@ inlinable_function_p (fn, id)
inlinable = 1;
/* Squirrel away the result so that we don't have to check again. */
DECL_UNINLINABLE (fn) = !inlinable;
DECL_UNINLINABLE (fn) = ! inlinable;
/* Even if this function is not itself too big to inline, it might
be that we've done so much inlining already that we don't want to
risk too much inlining any more and thus halve the acceptable
size. */
if (! LANG_DISREGARD_INLINE_LIMITS (fn)
&& ((DECL_NUM_STMTS (fn) + id->inlined_stmts) * INSNS_PER_STMT
&& ((DECL_NUM_STMTS (fn) + (id ? id->inlined_stmts : 0)) * INSNS_PER_STMT
> MAX_INLINE_INSNS)
&& DECL_NUM_STMTS (fn) * INSNS_PER_STMT > MAX_INLINE_INSNS / 4)
inlinable = 0;
......@@ -674,7 +710,7 @@ inlinable_function_p (fn, id)
/* If we don't have the function body available, we can't inline
it. */
if (!DECL_SAVED_TREE (fn))
if (! DECL_SAVED_TREE (fn))
inlinable = 0;
/* Check again, language hooks may have modified it. */
......@@ -683,7 +719,7 @@ inlinable_function_p (fn, id)
/* Don't do recursive inlining, either. We don't record this in
DECL_UNINLINABLE; we may be able to inline this function later. */
if (inlinable)
if (id)
{
size_t i;
......@@ -691,7 +727,7 @@ inlinable_function_p (fn, id)
if (VARRAY_TREE (id->fns, i) == fn)
return 0;
if (inlinable && DECL_INLINED_FNS (fn))
if (DECL_INLINED_FNS (fn))
{
int j;
tree inlined_fns = DECL_INLINED_FNS (fn);
......
......@@ -25,6 +25,7 @@ Boston, MA 02111-1307, USA. */
/* Function prototypes. */
void optimize_inline_calls PARAMS ((tree));
int tree_inlinable_function_p PARAMS ((tree));
tree walk_tree PARAMS ((tree*, walk_tree_fn, void*, void*));
tree walk_tree_without_duplicates PARAMS ((tree*, walk_tree_fn, void*));
tree copy_tree_r PARAMS ((tree*, int*, void*));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment