diff options
-rw-r--r-- | gcc/ChangeLog | 32 | ||||
-rw-r--r-- | gcc/Makefile.in | 4 | ||||
-rw-r--r-- | gcc/c-decl.c | 24 | ||||
-rw-r--r-- | gcc/c-lang.c | 175 | ||||
-rw-r--r-- | gcc/cse.c | 1 | ||||
-rw-r--r-- | gcc/integrate.c | 45 | ||||
-rw-r--r-- | gcc/tree-inline.c | 70 | ||||
-rw-r--r-- | gcc/tree-inline.h | 1 |
8 files changed, 311 insertions, 41 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index a1b97df..f8436b7 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,5 +1,37 @@ 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. diff --git a/gcc/Makefile.in b/gcc/Makefile.in index e16aede..3199e2a 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -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 \ diff --git a/gcc/c-decl.c b/gcc/c-decl.c index ac09d26..10e4270 100644 --- a/gcc/c-decl.c +++ b/gcc/c-decl.c @@ -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 diff --git a/gcc/c-lang.c b/gcc/c-lang.c index 36d73d2..7e7ebb7 100644 --- a/gcc/c-lang.c +++ b/gcc/c-lang.c @@ -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))); } diff --git a/gcc/integrate.c b/gcc/integrate.c index 9105dee..ddd1ce2 100644 --- a/gcc/integrate.c +++ b/gcc/integrate.c @@ -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 diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c index e24d39b..310ff88 100644 --- a/gcc/tree-inline.c +++ b/gcc/tree-inline.c @@ -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); diff --git a/gcc/tree-inline.h b/gcc/tree-inline.h index 6e00f96..0f36148 100644 --- a/gcc/tree-inline.h +++ b/gcc/tree-inline.h @@ -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*)); |