diff options
Diffstat (limited to 'gcc/tree-inline.c')
-rw-r--r-- | gcc/tree-inline.c | 1828 |
1 files changed, 1054 insertions, 774 deletions
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c index 622ae18a548..feb395f5c4b 100644 --- a/gcc/tree-inline.c +++ b/gcc/tree-inline.c @@ -39,17 +39,14 @@ Boston, MA 02111-1307, USA. */ #include "langhooks.h" #include "cgraph.h" #include "intl.h" -#include "diagnostic.h" +#include "tree-mudflap.h" #include "function.h" +#include "diagnostic.h" -/* This should be eventually be generalized to other languages, but - this would require a shared function-as-trees infrastructure. */ -#ifndef INLINER_FOR_JAVA -#include "c-common.h" -#else /* INLINER_FOR_JAVA */ -#include "parse.h" -#include "java-tree.h" -#endif /* INLINER_FOR_JAVA */ +/* I'm not real happy about this, but we need to handle gimple and + non-gimple trees. */ +#include "tree-iterator.h" +#include "tree-simple.h" /* 0 if we should not perform inlining. 1 if we should expand functions calls inline at the tree level. @@ -88,6 +85,8 @@ typedef struct inline_data this value is NULL, then return statements will simply be remapped as return statements, rather than as jumps. */ tree ret_label; + /* The VAR_DECL for the return value. */ + tree retvar; /* The map from local declarations in the inlined function to equivalents in the function into which it is being inlined. */ splay_tree decl_map; @@ -110,10 +109,19 @@ typedef struct inline_data struct cgraph_node *node; /* Callgraph node of currently inlined function. */ struct cgraph_node *current_node; + /* Statement iterator. We need this so we can keep the tree in + gimple form when we insert the inlined function. It is not + used when we are not dealing with gimple trees. */ + tree_stmt_iterator tsi; } inline_data; /* Prototypes. */ +/* The approximate number of instructions per statement. This number + need not be particularly accurate; it is used only to make + decisions about when a function is too big to inline. */ +#define INSNS_PER_STMT (10) + static tree declare_return_variable (inline_data *, tree, tree *); static tree copy_body_r (tree *, int *, void *); static tree copy_body (inline_data *); @@ -122,15 +130,14 @@ static void expand_calls_inline (tree *, inline_data *); static bool inlinable_function_p (tree); static tree remap_decl (tree, inline_data *); static tree remap_type (tree, inline_data *); -#ifndef INLINER_FOR_JAVA -static tree initialize_inlined_parameters (inline_data *, tree, tree); -static void remap_block (tree, tree, inline_data *); -static void copy_scope_stmt (tree *, int *, inline_data *); -#else /* INLINER_FOR_JAVA */ -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 initialize_inlined_parameters (inline_data *, tree, + tree, tree, tree); +static void remap_block (tree *, inline_data *); +static tree remap_decls (tree, inline_data *); +static void copy_bind_expr (tree *, int *, inline_data *); +static tree mark_local_for_remap_r (tree *, int *, void *); +static tree unsave_r (tree *, int *, void *); +static void declare_inline_vars (tree bind_expr, tree vars); /* Insert a tree->tree mapping for ID. Despite the name suggests that the trees should be variables, it is used for more than that. */ @@ -158,8 +165,13 @@ remap_decl (tree decl, inline_data *id) /* We only remap local variables in the current function. */ fn = VARRAY_TOP_TREE (id->fns); +#if 0 + /* We need to remap statics, too, so that they get expanded even if the + inline function is never emitted out of line. We might as well also + remap extern decls so that they show up in the debug info. */ if (! lang_hooks.tree_inlining.auto_var_in_fn_p (decl, fn)) return NULL_TREE; +#endif /* See if we have remapped this declaration. */ n = splay_tree_lookup (id->decl_map, (splay_tree_key) decl); @@ -185,7 +197,8 @@ remap_decl (tree decl, inline_data *id) walk_tree (&DECL_SIZE (t), copy_body_r, id, NULL); walk_tree (&DECL_SIZE_UNIT (t), copy_body_r, id, NULL); -#ifndef INLINER_FOR_JAVA +#if 0 + /* FIXME handle anon aggrs. */ if (! DECL_NAME (t) && TREE_TYPE (t) && lang_hooks.tree_inlining.anon_aggr_type_p (TREE_TYPE (t))) { @@ -205,7 +218,7 @@ remap_decl (tree decl, inline_data *id) } DECL_ANON_UNION_ELEMS (t) = nreverse (members); } -#endif /* not INLINER_FOR_JAVA */ +#endif /* Remember it, so that if we encounter this local entity again we can reuse this copy. */ @@ -213,7 +226,7 @@ remap_decl (tree decl, inline_data *id) return t; } - return (tree) n->value; + return unshare_expr ((tree) n->value); } static tree @@ -320,115 +333,47 @@ remap_type (tree type, inline_data *id) return new; } -#ifndef INLINER_FOR_JAVA -/* Copy the SCOPE_STMT_BLOCK associated with SCOPE_STMT to contain - remapped versions of the variables therein. And hook the new block - into the block-tree. If non-NULL, the DECLS are declarations to - add to use instead of the BLOCK_VARS in the old block. */ -#else /* INLINER_FOR_JAVA */ -/* Copy the BLOCK to contain remapped versions of the variables - therein. And hook the new block into the block-tree. */ -#endif /* INLINER_FOR_JAVA */ - -static void -#ifndef INLINER_FOR_JAVA -remap_block (tree scope_stmt, tree decls, inline_data *id) -#else /* INLINER_FOR_JAVA */ -remap_block (tree *block, tree decls, inline_data *id) -#endif /* INLINER_FOR_JAVA */ +static tree +remap_decls (tree decls, inline_data *id) { -#ifndef INLINER_FOR_JAVA - /* We cannot do this in the cleanup for a TARGET_EXPR since we do - not know whether or not expand_expr will actually write out the - code we put there. If it does not, then we'll have more BLOCKs - than block-notes, and things will go awry. At some point, we - should make the back-end handle BLOCK notes in a tidier way, - without requiring a strict correspondence to the block-tree; then - this check can go. */ - if (id->in_target_cleanup_p) - { - SCOPE_STMT_BLOCK (scope_stmt) = NULL_TREE; - return; - } + tree old_var; + tree new_decls = NULL_TREE; - /* If this is the beginning of a scope, remap the associated BLOCK. */ - if (SCOPE_BEGIN_P (scope_stmt) && SCOPE_STMT_BLOCK (scope_stmt)) + /* Remap its variables. */ + for (old_var = decls; old_var; old_var = TREE_CHAIN (old_var)) { - tree old_block; - tree new_block; - tree old_var; - tree fn; - - /* Make the new block. */ - old_block = SCOPE_STMT_BLOCK (scope_stmt); - new_block = make_node (BLOCK); - TREE_USED (new_block) = TREE_USED (old_block); - BLOCK_ABSTRACT_ORIGIN (new_block) = old_block; - SCOPE_STMT_BLOCK (scope_stmt) = new_block; - - /* Remap its variables. */ - for (old_var = decls ? decls : BLOCK_VARS (old_block); - old_var; - old_var = TREE_CHAIN (old_var)) - { - tree new_var; - - /* Remap the variable. */ - new_var = remap_decl (old_var, id); - /* If we didn't remap this variable, so we can't mess with - its TREE_CHAIN. If we remapped this variable to - something other than a declaration (say, if we mapped it - to a constant), then we must similarly omit any mention - of it here. */ - if (!new_var || !DECL_P (new_var)) - ; - else - { - TREE_CHAIN (new_var) = BLOCK_VARS (new_block); - BLOCK_VARS (new_block) = new_var; - } - } - /* We put the BLOCK_VARS in reverse order; fix that now. */ - BLOCK_VARS (new_block) = nreverse (BLOCK_VARS (new_block)); - fn = VARRAY_TREE (id->fns, 0); - if (id->cloning_p) - /* We're building a clone; DECL_INITIAL is still - error_mark_node, and current_binding_level is the parm - binding level. */ - lang_hooks.decls.insert_block (new_block); + tree new_var; + + /* Remap the variable. */ + new_var = remap_decl (old_var, id); + + /* If we didn't remap this variable, so we can't mess with its + TREE_CHAIN. If we remapped this variable to the return slot, it's + already declared somewhere else, so don't declare it here. */ + if (!new_var || new_var == id->retvar) + ; +#ifdef ENABLE_CHECKING + else if (!DECL_P (new_var)) + abort (); +#endif else { - /* Attach this new block after the DECL_INITIAL block for the - function into which this block is being inlined. In - rest_of_compilation we will straighten out the BLOCK tree. */ - tree *first_block; - if (DECL_INITIAL (fn)) - first_block = &BLOCK_CHAIN (DECL_INITIAL (fn)); - else - first_block = &DECL_INITIAL (fn); - BLOCK_CHAIN (new_block) = *first_block; - *first_block = new_block; + TREE_CHAIN (new_var) = new_decls; + new_decls = new_var; } - /* Remember the remapped block. */ - insert_decl_map (id, old_block, new_block); } - /* If this is the end of a scope, set the SCOPE_STMT_BLOCK to be the - remapped block. */ - else if (SCOPE_END_P (scope_stmt) && SCOPE_STMT_BLOCK (scope_stmt)) - { - splay_tree_node n; - /* Find this block in the table of remapped things. */ - n = splay_tree_lookup (id->decl_map, - (splay_tree_key) SCOPE_STMT_BLOCK (scope_stmt)); - if (! n) - abort (); - SCOPE_STMT_BLOCK (scope_stmt) = (tree) n->value; - } -#else /* INLINER_FOR_JAVA */ + return nreverse (new_decls); +} + +/* Copy the BLOCK to contain remapped versions of the variables + therein. And hook the new block into the block-tree. */ + +static void +remap_block (tree *block, inline_data *id) +{ tree old_block; tree new_block; - tree old_var; tree fn; /* Make the new block. */ @@ -436,82 +381,70 @@ remap_block (tree *block, tree decls, inline_data *id) new_block = make_node (BLOCK); TREE_USED (new_block) = TREE_USED (old_block); BLOCK_ABSTRACT_ORIGIN (new_block) = old_block; - BLOCK_SUBBLOCKS (new_block) = BLOCK_SUBBLOCKS (old_block); - TREE_SIDE_EFFECTS (new_block) = TREE_SIDE_EFFECTS (old_block); - TREE_TYPE (new_block) = TREE_TYPE (old_block); *block = new_block; /* Remap its variables. */ - for (old_var = decls ? decls : BLOCK_VARS (old_block); - old_var; - old_var = TREE_CHAIN (old_var)) - { - tree new_var; + BLOCK_VARS (new_block) = remap_decls (BLOCK_VARS (old_block), id); - /* All local class initialization flags go in the outermost - scope. */ - if (LOCAL_CLASS_INITIALIZATION_FLAG_P (old_var)) - { - /* We may already have one. */ - if (! splay_tree_lookup (id->decl_map, (splay_tree_key) old_var)) - { - tree outermost_block; - new_var = remap_decl (old_var, id); - DECL_ABSTRACT_ORIGIN (new_var) = NULL; - outermost_block = DECL_SAVED_TREE (current_function_decl); - TREE_CHAIN (new_var) = BLOCK_VARS (outermost_block); - BLOCK_VARS (outermost_block) = new_var; - } - continue; - } - - /* Remap the variable. */ - new_var = remap_decl (old_var, id); - /* If we didn't remap this variable, so we can't mess with - its TREE_CHAIN. If we remapped this variable to - something other than a declaration (say, if we mapped it - to a constant), then we must similarly omit any mention - of it here. */ - if (!new_var || !DECL_P (new_var)) - ; + fn = VARRAY_TREE (id->fns, 0); +#if 1 + /* FIXME! It shouldn't be so hard to manage blocks. Rebuilding them in + rest_of_compilation is a good start. */ + if (id->cloning_p) + /* We're building a clone; DECL_INITIAL is still + error_mark_node, and current_binding_level is the parm + binding level. */ + (*lang_hooks.decls.insert_block) (new_block); + else + { + /* Attach this new block after the DECL_INITIAL block for the + function into which this block is being inlined. In + rest_of_compilation we will straighten out the BLOCK tree. */ + tree *first_block; + if (DECL_INITIAL (fn)) + first_block = &BLOCK_CHAIN (DECL_INITIAL (fn)); else - { - TREE_CHAIN (new_var) = BLOCK_VARS (new_block); - BLOCK_VARS (new_block) = new_var; - } + first_block = &DECL_INITIAL (fn); + BLOCK_CHAIN (new_block) = *first_block; + *first_block = new_block; } - /* We put the BLOCK_VARS in reverse order; fix that now. */ - BLOCK_VARS (new_block) = nreverse (BLOCK_VARS (new_block)); - fn = VARRAY_TREE (id->fns, 0); +#endif /* Remember the remapped block. */ - splay_tree_insert (id->decl_map, - (splay_tree_key) old_block, - (splay_tree_value) new_block); -#endif /* INLINER_FOR_JAVA */ + insert_decl_map (id, old_block, new_block); } -#ifndef INLINER_FOR_JAVA -/* Copy the SCOPE_STMT pointed to by TP. */ - static void -copy_scope_stmt (tree *tp, int *walk_subtrees, inline_data *id) +copy_statement_list (tree *tp) { - tree block; + tree_stmt_iterator oi, ni; + tree new; + + new = alloc_stmt_list (); + ni = tsi_start (new); + oi = tsi_start (*tp); + *tp = new; + + for (; !tsi_end_p (oi); tsi_next (&oi)) + tsi_link_after (&ni, tsi_stmt (oi), TSI_NEW_STMT); +} - /* Remember whether or not this statement was nullified. When - making a copy, copy_tree_r always sets SCOPE_NULLIFIED_P (and - doesn't copy the SCOPE_STMT_BLOCK) to free callers from having to - deal with copying BLOCKs if they do not wish to do so. */ - block = SCOPE_STMT_BLOCK (*tp); +static void +copy_bind_expr (tree *tp, int *walk_subtrees, inline_data *id) +{ + tree block = BIND_EXPR_BLOCK (*tp); /* Copy (and replace) the statement. */ copy_tree_r (tp, walk_subtrees, NULL); - /* Restore the SCOPE_STMT_BLOCK. */ - SCOPE_STMT_BLOCK (*tp) = block; + if (block) + { + remap_block (&block, id); + BIND_EXPR_BLOCK (*tp) = block; + } - /* Remap the associated block. */ - remap_block (*tp, NULL_TREE, id); + if (BIND_EXPR_VARS (*tp)) + /* This will remap a lot of the same decls again, but this should be + harmless. */ + BIND_EXPR_VARS (*tp) = remap_decls (BIND_EXPR_VARS (*tp), id); } -#endif /* not INLINER_FOR_JAVA */ /* Called from copy_body via walk_tree. DATA is really an `inline_data *'. */ @@ -534,60 +467,36 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data) abort (); #endif -#ifdef INLINER_FOR_JAVA - if (TREE_CODE (*tp) == BLOCK) - remap_block (tp, NULL_TREE, id); -#endif - /* If this is a RETURN_STMT, change it into an EXPR_STMT and a GOTO_STMT with the RET_LABEL as its target. */ -#ifndef INLINER_FOR_JAVA - if (TREE_CODE (*tp) == RETURN_STMT && id->ret_label && !id->saving_p) -#else /* INLINER_FOR_JAVA */ - if (TREE_CODE (*tp) == RETURN_EXPR && id->ret_label && !id->saving_p) -#endif /* INLINER_FOR_JAVA */ + if (TREE_CODE (*tp) == RETURN_EXPR && id->ret_label) { tree return_stmt = *tp; tree goto_stmt; - /* Build the GOTO_STMT. */ -#ifndef INLINER_FOR_JAVA - goto_stmt = build_stmt (GOTO_STMT, id->ret_label); - TREE_CHAIN (goto_stmt) = TREE_CHAIN (return_stmt); - GOTO_FAKE_P (goto_stmt) = 1; -#else /* INLINER_FOR_JAVA */ + /* Build the GOTO_EXPR. */ tree assignment = TREE_OPERAND (return_stmt, 0); goto_stmt = build1 (GOTO_EXPR, void_type_node, id->ret_label); - TREE_SIDE_EFFECTS (goto_stmt) = 1; -#endif /* INLINER_FOR_JAVA */ + TREE_USED (id->ret_label) = 1; /* If we're returning something, just turn that into an assignment into the equivalent of the original RESULT_DECL. */ -#ifndef INLINER_FOR_JAVA - if (RETURN_STMT_EXPR (return_stmt)) - { - *tp = build_stmt (EXPR_STMT, - RETURN_STMT_EXPR (return_stmt)); - STMT_IS_FULL_EXPR_P (*tp) = 1; - /* And then jump to the end of the function. */ - TREE_CHAIN (*tp) = goto_stmt; - } -#else /* INLINER_FOR_JAVA */ if (assignment) - { - copy_body_r (&assignment, walk_subtrees, data); - *tp = build (COMPOUND_EXPR, void_type_node, assignment, goto_stmt); - TREE_SIDE_EFFECTS (*tp) = 1; - } -#endif /* INLINER_FOR_JAVA */ + { + /* Do not create a statement containing a naked RESULT_DECL. */ + if (lang_hooks.gimple_before_inlining) + if (TREE_CODE (assignment) == RESULT_DECL) + gimplify_stmt (&assignment); + + *tp = build (BIND_EXPR, void_type_node, NULL_TREE, NULL_TREE, + make_node (BLOCK)); + append_to_statement_list (assignment, &BIND_EXPR_BODY (*tp)); + append_to_statement_list (goto_stmt, &BIND_EXPR_BODY (*tp)); + } /* If we're not returning anything just do the jump. */ else *tp = goto_stmt; - - /* We can't replace return label while inlining function - because it is in the outer function. */ - insert_decl_map (id, id->ret_label, id->ret_label); } /* Local variables and labels need to be replaced by equivalent variables. We don't want to copy static variables; there's only @@ -610,18 +519,16 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data) && DECL_CONTEXT (*tp) != VARRAY_TREE (id->fns, 0)) abort (); #endif + else if (TREE_CODE (*tp) == STATEMENT_LIST) + copy_statement_list (tp); else if (TREE_CODE (*tp) == SAVE_EXPR) remap_save_expr (tp, id->decl_map, VARRAY_TREE (id->fns, 0), walk_subtrees); else if (TREE_CODE (*tp) == UNSAVE_EXPR) /* UNSAVE_EXPRs should not be generated until expansion time. */ abort (); -#ifndef INLINER_FOR_JAVA - /* For a SCOPE_STMT, we must copy the associated block so that we - can write out debugging information for the inlined variables. */ - else if (TREE_CODE (*tp) == SCOPE_STMT && !id->in_target_cleanup_p) - copy_scope_stmt (tp, walk_subtrees, id); -#else /* INLINER_FOR_JAVA */ + else if (TREE_CODE (*tp) == BIND_EXPR) + copy_bind_expr (tp, walk_subtrees, id); else if (TREE_CODE (*tp) == LABELED_BLOCK_EXPR) { /* We need a new copy of this labeled block; the EXIT_BLOCK_EXPR @@ -642,7 +549,6 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data) *tp = copy_node (*tp); TREE_OPERAND (*tp, 0) = (tree) n->value; } -#endif /* INLINER_FOR_JAVA */ /* Types may need remapping as well. */ else if (TYPE_P (*tp)) *tp = remap_type (*tp, id); @@ -692,7 +598,32 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data) value = (tree) n->value; if (TREE_CODE (value) == INDIRECT_REF) { - *tp = convert (TREE_TYPE (*tp), TREE_OPERAND (value, 0)); + /* Assume that the argument types properly match the + parameter types. We can't compare them well enough + without a comptypes langhook, and we don't want to + call convert and introduce a NOP_EXPR to convert + between two equivalent types (i.e. that only differ + in use of typedef names). */ + *tp = TREE_OPERAND (value, 0); + return copy_body_r (tp, walk_subtrees, data); + } + } + } + else if (TREE_CODE (*tp) == INDIRECT_REF) + { + /* Get rid of *& from inline substitutions that can happen when a + pointer argument is an ADDR_EXPR. */ + tree decl = TREE_OPERAND (*tp, 0), value; + splay_tree_node n; + + n = splay_tree_lookup (id->decl_map, (splay_tree_key) decl); + if (n) + { + value = (tree) n->value; + STRIP_NOPS (value); + if (TREE_CODE (value) == ADDR_EXPR) + { + *tp = TREE_OPERAND (value, 0); return copy_body_r (tp, walk_subtrees, data); } } @@ -716,7 +647,7 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data) abort (); } } - else if (!id->cloning_p) + else { struct cgraph_edge *edge; @@ -760,45 +691,142 @@ copy_body (inline_data *id) return body; } +static void +setup_one_parameter (inline_data *id, tree p, tree value, + tree fn, tree *init_stmts, tree *vars, + bool *gimplify_init_stmts_p) +{ + tree init_stmt; + tree var; + tree var_sub; + + /* 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) + && value && !TREE_SIDE_EFFECTS (value)) + { + /* We can't risk substituting complex expressions. They + might contain variables that will be assigned to later. + Theoretically, we could check the expression to see if + all of the variables that determine its value are + read-only, but we don't bother. */ + if ((TREE_CONSTANT (value) || TREE_READONLY_DECL_P (value)) + /* We may produce non-gimple trees by adding NOPs or introduce + invalid sharing when operand is not really constant. + It is not big deal to prohibit constant propagation here as + we will constant propagate in DOM1 pass anyway. */ + && (!lang_hooks.gimple_before_inlining + || (is_gimple_min_invariant (value) + && TREE_TYPE (value) == TREE_TYPE (p)))) + { + /* If this is a declaration, wrap it a NOP_EXPR so that + we don't try to put the VALUE on the list of BLOCK_VARS. */ + if (DECL_P (value)) + value = build1 (NOP_EXPR, TREE_TYPE (value), value); + + /* If this is a constant, make sure it has the right type. */ + else if (TREE_TYPE (value) != TREE_TYPE (p)) + value = fold (build1 (NOP_EXPR, TREE_TYPE (p), value)); + + insert_decl_map (id, p, value); + return; + } + } + + /* Make an equivalent VAR_DECL. */ + var = copy_decl_for_inlining (p, fn, VARRAY_TREE (id->fns, 0)); + + /* See if the frontend wants to pass this by invisible reference. If + so, our new VAR_DECL will have REFERENCE_TYPE, and we need to + replace uses of the PARM_DECL with dereferences. */ + if (TREE_TYPE (var) != TREE_TYPE (p) + && POINTER_TYPE_P (TREE_TYPE (var)) + && TREE_TYPE (TREE_TYPE (var)) == TREE_TYPE (p)) + { + insert_decl_map (id, var, var); + var_sub = build1 (INDIRECT_REF, TREE_TYPE (p), var); + } + else + var_sub = var; + + /* Register the VAR_DECL as the equivalent for the PARM_DECL; + that way, when the PARM_DECL is encountered, it will be + automatically replaced by the VAR_DECL. */ + insert_decl_map (id, p, var_sub); + + /* Declare this new variable. */ + TREE_CHAIN (var) = *vars; + *vars = var; + + /* Make gimplifier happy about this variable. */ + var->decl.seen_in_bind_expr = lang_hooks.gimple_before_inlining; + + /* Even if P was TREE_READONLY, the new VAR should not be. + In the original code, we would have constructed a + temporary, and then the function body would have never + changed the value of P. However, now, we will be + constructing VAR directly. The constructor body may + change its value multiple times as it is being + constructed. Therefore, it must not be TREE_READONLY; + the back-end assumes that TREE_READONLY variable is + assigned to only once. */ + if (TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (p))) + TREE_READONLY (var) = 0; + + /* Initialize this VAR_DECL from the equivalent argument. Convert + the argument to the proper type in case it was promoted. */ + if (value) + { + tree rhs = convert (TREE_TYPE (var), value); + + if (rhs == error_mark_node) + return; + + /* We want to use MODIFY_EXPR, not INIT_EXPR here so that we + keep our trees in gimple form. */ + init_stmt = build (MODIFY_EXPR, TREE_TYPE (var), var, rhs); + append_to_statement_list (init_stmt, init_stmts); + + /* If we did not create a gimple value and we did not create a gimple + cast of a gimple value, then we will need to gimplify INIT_STMTS + at the end. Note that is_gimple_cast only checks the outer + tree code, not its operand. Thus the explicit check that it's + operand is a gimple value. */ + if (!is_gimple_val (rhs) + && (!is_gimple_cast (rhs) + || !is_gimple_val (TREE_OPERAND (rhs, 0)))) + *gimplify_init_stmts_p = true; + } +} + /* Generate code to initialize the parameters of the function at the top of the stack in ID from the ARGS (presented as a TREE_LIST). */ static tree -#ifndef INLINER_FOR_JAVA -initialize_inlined_parameters (inline_data *id, tree args, tree fn) -#else /* INLINER_FOR_JAVA */ -initialize_inlined_parameters (inline_data *id, tree args, tree fn, tree block) -#endif /* INLINER_FOR_JAVA */ +initialize_inlined_parameters (inline_data *id, tree args, tree static_chain, + tree fn, tree bind_expr) { - tree init_stmts; + tree init_stmts = NULL_TREE; tree parms; tree a; tree p; -#ifdef INLINER_FOR_JAVA tree vars = NULL_TREE; -#endif /* INLINER_FOR_JAVA */ + bool gimplify_init_stmts_p = false; int argnum = 0; /* Figure out what the parameters are. */ parms = DECL_ARGUMENTS (fn); - if (fn == current_function_decl && cfun->saved_args) + if (fn == current_function_decl) parms = cfun->saved_args; - /* Start with no initializations whatsoever. */ - init_stmts = NULL_TREE; - /* Loop through the parameter declarations, replacing each with an equivalent VAR_DECL, appropriately initialized. */ for (p = parms, a = args; p; a = a ? TREE_CHAIN (a) : a, p = TREE_CHAIN (p)) { -#ifndef INLINER_FOR_JAVA - tree init_stmt; - tree cleanup; -#endif /* not INLINER_FOR_JAVA */ - tree var; tree value; - tree var_sub; ++argnum; @@ -806,148 +834,34 @@ initialize_inlined_parameters (inline_data *id, tree args, tree fn, tree block) value = lang_hooks.tree_inlining.convert_parm_for_inlining (p, a ? TREE_VALUE (a) : NULL_TREE, fn, argnum); - /* 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) - && value && !TREE_SIDE_EFFECTS (value)) - { - /* Simplify the value, if possible. */ - 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. - Theoretically, we could check the expression to see if - all of the variables that determine its value are - read-only, but we don't bother. */ - if (TREE_CONSTANT (value) || TREE_READONLY_DECL_P (value)) - { - /* If this is a declaration, wrap it a NOP_EXPR so that - we don't try to put the VALUE on the list of - BLOCK_VARS. */ - if (DECL_P (value)) - value = build1 (NOP_EXPR, TREE_TYPE (value), value); - - /* If this is a constant, make sure it has the right type. */ - else if (TREE_TYPE (value) != TREE_TYPE (p)) - value = fold (build1 (NOP_EXPR, TREE_TYPE (p), value)); - - insert_decl_map (id, p, value); - continue; - } - } - - /* Make an equivalent VAR_DECL. */ - var = copy_decl_for_inlining (p, fn, VARRAY_TREE (id->fns, 0)); - - /* See if the frontend wants to pass this by invisible reference. If - so, our new VAR_DECL will have REFERENCE_TYPE, and we need to - replace uses of the PARM_DECL with dereferences. */ - if (TREE_TYPE (var) != TREE_TYPE (p) - && POINTER_TYPE_P (TREE_TYPE (var)) - && TREE_TYPE (TREE_TYPE (var)) == TREE_TYPE (p)) - var_sub = build1 (INDIRECT_REF, TREE_TYPE (p), var); - else - var_sub = var; - - /* Register the VAR_DECL as the equivalent for the PARM_DECL; - that way, when the PARM_DECL is encountered, it will be - automatically replaced by the VAR_DECL. */ - insert_decl_map (id, p, var_sub); - - /* Declare this new variable. */ -#ifndef INLINER_FOR_JAVA - init_stmt = build_stmt (DECL_STMT, var); - TREE_CHAIN (init_stmt) = init_stmts; - init_stmts = init_stmt; -#else /* INLINER_FOR_JAVA */ - TREE_CHAIN (var) = vars; - vars = var; -#endif /* INLINER_FOR_JAVA */ - - /* Initialize this VAR_DECL from the equivalent argument. If - the argument is an object, created via a constructor or copy, - this will not result in an extra copy: the TARGET_EXPR - representing the argument will be bound to VAR, and the - object will be constructed in VAR. */ - if (! TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (p))) -#ifndef INLINER_FOR_JAVA - DECL_INITIAL (var) = value; - else - { - /* Even if P was TREE_READONLY, the new VAR should not be. - In the original code, we would have constructed a - temporary, and then the function body would have never - changed the value of P. However, now, we will be - constructing VAR directly. The constructor body may - change its value multiple times as it is being - constructed. Therefore, it must not be TREE_READONLY; - the back-end assumes that TREE_READONLY variable is - assigned to only once. */ - TREE_READONLY (var) = 0; - - /* Build a run-time initialization. */ - init_stmt = build_stmt (EXPR_STMT, - build (INIT_EXPR, TREE_TYPE (p), - var, value)); - /* Add this initialization to the list. Note that we want the - declaration *after* the initialization because we are going - to reverse all the initialization statements below. */ - TREE_CHAIN (init_stmt) = init_stmts; - init_stmts = init_stmt; - } - - /* See if we need to clean up the declaration. */ - cleanup = lang_hooks.maybe_build_cleanup (var); - if (cleanup) - { - tree cleanup_stmt; - /* Build the cleanup statement. */ - cleanup_stmt = build_stmt (CLEANUP_STMT, var, cleanup); - /* Add it to the *front* of the list; the list will be - reversed below. */ - TREE_CHAIN (cleanup_stmt) = init_stmts; - init_stmts = cleanup_stmt; - } -#else /* INLINER_FOR_JAVA */ - { - tree assignment = build (MODIFY_EXPR, TREE_TYPE (p), var, value); - init_stmts = add_stmt_to_compound (init_stmts, TREE_TYPE (p), - assignment); - } - else - { - /* Java objects don't ever need constructing when being - passed as arguments because only call by reference is - supported. */ - abort (); - } -#endif /* INLINER_FOR_JAVA */ + setup_one_parameter (id, p, value, fn, &init_stmts, &vars, + &gimplify_init_stmts_p); } -#ifndef INLINER_FOR_JAVA /* Evaluate trailing arguments. */ for (; a; a = TREE_CHAIN (a)) { - tree init_stmt; tree value = TREE_VALUE (a); + append_to_statement_list (value, &init_stmts); + } - if (! value || ! TREE_SIDE_EFFECTS (value)) - continue; + /* Initialize the static chain. */ + p = DECL_STRUCT_FUNCTION (fn)->static_chain_decl; + if (p) + { + /* No static chain? Seems like a bug in tree-nested.c. */ + if (!static_chain) + abort (); - init_stmt = build_stmt (EXPR_STMT, value); - TREE_CHAIN (init_stmt) = init_stmts; - init_stmts = init_stmt; + setup_one_parameter (id, p, static_chain, fn, &init_stmts, &vars, + &gimplify_init_stmts_p); } - /* The initialization statements have been built up in reverse - order. Straighten them out now. */ - return nreverse (init_stmts); -#else /* INLINER_FOR_JAVA */ - BLOCK_VARS (block) = nreverse (vars); + if (gimplify_init_stmts_p && lang_hooks.gimple_before_inlining) + gimplify_body (&init_stmts, fn); + + declare_inline_vars (bind_expr, vars); return init_stmts; -#endif /* INLINER_FOR_JAVA */ } /* Declare a return variable to replace the RESULT_DECL for the @@ -955,71 +869,58 @@ initialize_inlined_parameters (inline_data *id, tree args, tree fn, tree block) The USE_STMT is filled in to contain a use of the declaration to indicate the return value of the function. */ -#ifndef INLINER_FOR_JAVA -static tree -declare_return_variable (struct inline_data *id, tree return_slot_addr, - tree *use_stmt) -#else /* INLINER_FOR_JAVA */ static tree -declare_return_variable (struct inline_data *id, tree return_slot_addr, - tree *var) -#endif /* INLINER_FOR_JAVA */ +declare_return_variable (inline_data *id, tree return_slot_addr, tree *use_p) { tree fn = VARRAY_TOP_TREE (id->fns); tree result = DECL_RESULT (fn); -#ifndef INLINER_FOR_JAVA - tree var; -#endif /* not INLINER_FOR_JAVA */ int need_return_decl = 1; + tree var; /* We don't need to do anything for functions that don't return anything. */ if (!result || VOID_TYPE_P (TREE_TYPE (result))) { -#ifndef INLINER_FOR_JAVA - *use_stmt = NULL_TREE; -#else /* INLINER_FOR_JAVA */ - *var = NULL_TREE; -#endif /* INLINER_FOR_JAVA */ + *use_p = NULL_TREE; return NULL_TREE; } -#ifndef INLINER_FOR_JAVA var = (lang_hooks.tree_inlining.copy_res_decl_for_inlining (result, fn, VARRAY_TREE (id->fns, 0), id->decl_map, &need_return_decl, return_slot_addr)); + + /* Do not have the rest of GCC warn about this variable as it should + not be visible to the user. */ + TREE_NO_WARNING (var) = 1; /* Register the VAR_DECL as the equivalent for the RESULT_DECL; that way, when the RESULT_DECL is encountered, it will be automatically replaced by the VAR_DECL. */ insert_decl_map (id, result, var); - /* Build the USE_STMT. If the return type of the function was + /* Remember this so we can ignore it in remap_decls. */ + id->retvar = var; + + /* Build the use expr. 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); + if (return_slot_addr) + /* The function returns through an explicit return slot, not a normal + return value. */ + *use_p = NULL_TREE; + else if (TREE_TYPE (var) == TREE_TYPE (TREE_TYPE (fn))) + *use_p = var; + else if (TREE_CODE (var) == INDIRECT_REF) + *use_p = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (fn)), + TREE_OPERAND (var, 0)); + else if (TREE_ADDRESSABLE (TREE_TYPE (var))) + abort (); else - *use_stmt = build_stmt (EXPR_STMT, - build1 (NOP_EXPR, TREE_TYPE (TREE_TYPE (fn)), - var)); - TREE_ADDRESSABLE (*use_stmt) = 1; + *use_p = build1 (NOP_EXPR, TREE_TYPE (TREE_TYPE (fn)), var); /* Build the declaration statement if FN does not return an aggregate. */ if (need_return_decl) - return build_stmt (DECL_STMT, var); -#else /* INLINER_FOR_JAVA */ - *var = (lang_hooks.tree_inlining.copy_res_decl_for_inlining - (result, fn, VARRAY_TREE (id->fns, 0), id->decl_map, - &need_return_decl, return_slot_addr)); - - splay_tree_insert (id->decl_map, - (splay_tree_key) result, - (splay_tree_value) *var); - DECL_IGNORED_P (*var) = 1; - if (need_return_decl) - return *var; -#endif /* INLINER_FOR_JAVA */ + return var; /* If FN does return an aggregate, there's no need to declare the return variable; we're using a variable in our caller's frame. */ else @@ -1073,7 +974,7 @@ inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED, return node; } - if (DECL_BUILT_IN (t)) + if (DECL_BUILT_IN_CLASS (t) == BUILT_IN_NORMAL) switch (DECL_FUNCTION_CODE (t)) { /* We cannot inline functions that take a variable number of @@ -1082,49 +983,48 @@ inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED, case BUILT_IN_STDARG_START: case BUILT_IN_NEXT_ARG: case BUILT_IN_VA_END: - { - inline_forbidden_reason - = N_("%Jfunction '%F' can never be inlined because it " - "uses variable argument lists"); - return node; - } + inline_forbidden_reason + = N_("%Jfunction '%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 - = N_("%Jfunction '%F' can never be inlined because " - "it uses setjmp-longjmp exception handling"); - return node; - } - } + /* 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. */ + inline_forbidden_reason + = N_("%Jfunction '%F' can never be inlined because " + "it uses setjmp-longjmp exception handling"); + return node; + + case BUILT_IN_NONLOCAL_GOTO: + /* Similarly. */ + inline_forbidden_reason + = N_("%Jfunction '%F' can never be inlined because " + "it uses non-local goto"); + 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))) + case BIND_EXPR: + for (t = BIND_EXPR_VARS (node); t ; t = TREE_CHAIN (t)) { - inline_forbidden_reason - = N_("%Jfunction '%F' can never be inlined " - "because it contains a nested function"); - return node; + /* We cannot inline functions that contain other functions. */ + if (TREE_CODE (t) == FUNCTION_DECL && DECL_INITIAL (t)) + { + inline_forbidden_reason + = N_("%Jfunction '%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); @@ -1139,17 +1039,19 @@ inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED, "because it contains a computed goto"); return node; } + break; - /* We cannot inline a nested function that jumps to a nonlocal - label. */ - if (TREE_CODE (t) == LABEL_DECL && DECL_CONTEXT (t) != fn) + case LABEL_EXPR: + t = TREE_OPERAND (node, 0); + if (DECL_NONLOCAL (t)) { + /* We cannot inline a function that receives a non-local goto + because we cannot remap the destination label used in the + function that is performing the non-local goto. */ inline_forbidden_reason = N_("%Jfunction '%F' can never be inlined " - "because it contains a nonlocal goto"); - return node; + "because it receives a non-local goto"); } - break; case RECORD_TYPE: @@ -1172,7 +1074,7 @@ inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED, "because it uses variable sized variables"); return node; } -#endif + default: break; } @@ -1271,6 +1173,223 @@ inlinable_function_p (tree fn) return inlinable; } +/* Used by estimate_num_insns. Estimate number of instructions seen + by given statement. */ +static tree +estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data) +{ + int *count = data; + tree x = *tp; + + if (TYPE_P (x) || DECL_P (x)) + { + *walk_subtrees = 0; + return NULL; + } + /* Assume that constants and references counts nothing. These should + be majorized by amount of operations among them we count later + and are common target of CSE and similar optimizations. */ + if (TREE_CODE_CLASS (TREE_CODE (x)) == 'c' + || TREE_CODE_CLASS (TREE_CODE (x)) == 'r') + return NULL; + switch (TREE_CODE (x)) + { + /* Containers have no cost. */ + case TREE_LIST: + case TREE_VEC: + case BLOCK: + case COMPONENT_REF: + case BIT_FIELD_REF: + case INDIRECT_REF: + case BUFFER_REF: + case ARRAY_REF: + case ARRAY_RANGE_REF: + case VTABLE_REF: + case EXC_PTR_EXPR: /* ??? */ + case FILTER_EXPR: /* ??? */ + case COMPOUND_EXPR: + case BIND_EXPR: + case LABELED_BLOCK_EXPR: + case WITH_CLEANUP_EXPR: + case NOP_EXPR: + case VIEW_CONVERT_EXPR: + case SAVE_EXPR: + case UNSAVE_EXPR: + case ADDR_EXPR: + case REFERENCE_EXPR: + case COMPLEX_EXPR: + case REALPART_EXPR: + case IMAGPART_EXPR: + case EXIT_BLOCK_EXPR: + case CASE_LABEL_EXPR: + case SSA_NAME: + case CATCH_EXPR: + case EH_FILTER_EXPR: + case STATEMENT_LIST: + case ERROR_MARK: + case NON_LVALUE_EXPR: + case ENTRY_VALUE_EXPR: + case FDESC_EXPR: + case VA_ARG_EXPR: + case TRY_CATCH_EXPR: + case TRY_FINALLY_EXPR: + case LABEL_EXPR: + case GOTO_EXPR: + case RETURN_EXPR: + case EXIT_EXPR: + case LOOP_EXPR: + case EUSE_NODE: + case EKILL_NODE: + case EPHI_NODE: + case EEXIT_NODE: + case PHI_NODE: + break; + /* We don't account constants for now. Assume that the cost is amortized + by operations that do use them. We may re-consider this decision once + we are able to optimize the tree before estimating it's size and break + out static initializers. */ + case IDENTIFIER_NODE: + case INTEGER_CST: + case REAL_CST: + case COMPLEX_CST: + case VECTOR_CST: + case STRING_CST: + *walk_subtrees = 0; + return NULL; + /* Recognize assignments of large structures and constructors of + big arrays. */ + case INIT_EXPR: + case TARGET_EXPR: + case MODIFY_EXPR: + case CONSTRUCTOR: + { + HOST_WIDE_INT size; + + size = int_size_in_bytes (TREE_TYPE (x)); + + if (size < 0 || size > MOVE_MAX_PIECES * MOVE_RATIO) + *count += 10; + else + *count += ((size + MOVE_MAX_PIECES - 1) / MOVE_MAX_PIECES); + } + break; + + /* Assign cost of 1 to usual operations. + ??? We may consider mapping RTL costs to this. */ + case COND_EXPR: + + case PLUS_EXPR: + case MINUS_EXPR: + case MULT_EXPR: + + case FIX_TRUNC_EXPR: + case FIX_CEIL_EXPR: + case FIX_FLOOR_EXPR: + case FIX_ROUND_EXPR: + + case NEGATE_EXPR: + case FLOAT_EXPR: + case MIN_EXPR: + case MAX_EXPR: + case ABS_EXPR: + + case LSHIFT_EXPR: + case RSHIFT_EXPR: + case LROTATE_EXPR: + case RROTATE_EXPR: + + case BIT_IOR_EXPR: + case BIT_XOR_EXPR: + case BIT_AND_EXPR: + case BIT_NOT_EXPR: + + case TRUTH_ANDIF_EXPR: + case TRUTH_ORIF_EXPR: + case TRUTH_AND_EXPR: + case TRUTH_OR_EXPR: + case TRUTH_XOR_EXPR: + case TRUTH_NOT_EXPR: + + case LT_EXPR: + case LE_EXPR: + case GT_EXPR: + case GE_EXPR: + case EQ_EXPR: + case NE_EXPR: + case ORDERED_EXPR: + case UNORDERED_EXPR: + + case UNLT_EXPR: + case UNLE_EXPR: + case UNGT_EXPR: + case UNGE_EXPR: + case UNEQ_EXPR: + + case CONVERT_EXPR: + + case CONJ_EXPR: + + case PREDECREMENT_EXPR: + case PREINCREMENT_EXPR: + case POSTDECREMENT_EXPR: + case POSTINCREMENT_EXPR: + + case SWITCH_EXPR: + + case ASM_EXPR: + + case RESX_EXPR: + *count++; + break; + + /* Few special cases of expensive operations. This is usefull + to avoid inlining on functions having too many of these. */ + case TRUNC_DIV_EXPR: + case CEIL_DIV_EXPR: + case FLOOR_DIV_EXPR: + case ROUND_DIV_EXPR: + case EXACT_DIV_EXPR: + case TRUNC_MOD_EXPR: + case CEIL_MOD_EXPR: + case FLOOR_MOD_EXPR: + case ROUND_MOD_EXPR: + case RDIV_EXPR: + *count += 10; + break; + case CALL_EXPR: + { + tree decl = get_callee_fndecl (x); + + if (decl && DECL_BUILT_IN (decl)) + switch (DECL_FUNCTION_CODE (decl)) + { + case BUILT_IN_CONSTANT_P: + *walk_subtrees = 0; + return NULL_TREE; + case BUILT_IN_EXPECT: + return NULL_TREE; + default: + break; + } + *count += 10; + break; + } + default: + /* Abort here se we know we don't miss any nodes. */ + abort (); + } + return NULL; +} + +/* Estimate number of instructions that will be created by expanding EXPR. */ +int +estimate_num_insns (tree expr) +{ + int num = 0; + walk_tree_without_duplicates (&expr, estimate_num_insns_1, &num); + return num; +} + /* If *TP is a CALL_EXPR, replace it with its inline expansion. */ static tree @@ -1280,19 +1399,16 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data) tree t; tree expr; tree stmt; -#ifndef INLINER_FOR_JAVA - tree chain; - tree scope_stmt; - tree use_stmt; -#else /* INLINER_FOR_JAVA */ - tree retvar; -#endif /* INLINER_FOR_JAVA */ + tree use_retvar; + tree decl; tree fn; tree arg_inits; tree *inlined_body; + tree inline_result; splay_tree st; tree args; tree return_slot_addr; + location_t saved_location; struct cgraph_edge *edge; const char *reason; @@ -1300,11 +1416,17 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data) id = (inline_data *) data; t = *tp; + /* Set input_location here so we get the right instantiation context + if we call instantiate_decl from inlinable_function_p. */ + saved_location = input_location; + if (EXPR_HAS_LOCATION (t)) + input_location = EXPR_LOCATION (t); + /* Recurse, but letting recursive invocations know that we are inside the body of a TARGET_EXPR. */ if (TREE_CODE (*tp) == TARGET_EXPR) { -#ifndef INLINER_FOR_JAVA +#if 0 int i, len = first_rtl_op (TARGET_EXPR); /* We're walking our own subtrees. */ @@ -1323,23 +1445,8 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data) --id->in_target_cleanup_p; } - return NULL_TREE; -#else /* INLINER_FOR_JAVA */ - abort (); -#endif /* INLINER_FOR_JAVA */ - } - else if (TREE_CODE (t) == EXPR_WITH_FILE_LOCATION) - { - /* We're walking the subtree directly. */ - *walk_subtrees = 0; - /* Update the source position. */ - push_srcloc (EXPR_WFL_FILENAME (t), EXPR_WFL_LINENO (t)); - walk_tree (&EXPR_WFL_NODE (t), expand_call_inline, data, - id->tree_pruner); - /* Restore the original source position. */ - pop_srcloc (); - - return NULL_TREE; + goto egress; +#endif } if (TYPE_P (t)) @@ -1350,13 +1457,13 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data) /* From here on, we're only interested in CALL_EXPRs. */ if (TREE_CODE (t) != CALL_EXPR) - return NULL_TREE; + goto egress; /* First, see if we can figure out what function is being called. If we cannot, then there is no hope of inlining the function. */ fn = get_callee_fndecl (t); if (!fn) - return NULL_TREE; + goto egress; /* Turn forward declarations into real ones. */ fn = cgraph_node (fn)->decl; @@ -1376,7 +1483,7 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data) /* Objective C and fortran still calls tree_rest_of_compilation directly. Kill this check once this is fixed. */ if (!id->current_node->analyzed) - return NULL_TREE; + goto egress; edge = cgraph_edge (id->current_node, t); @@ -1386,12 +1493,15 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data) { struct cgraph_node *dest = cgraph_node (fn); - /* FN must have address taken so it can be passed as argument. */ + /* We have missing edge in the callgraph. This can happen in one case + where previous inlining turned indirect call into direct call by + constant propagating arguments. In all other cases we hit a bug + (incorrect node sharing is most common reason for missing edges. */ if (!dest->needed) abort (); cgraph_create_edge (id->node, dest, t)->inline_failed = N_("originally indirect function call not considered for inlining"); - return NULL_TREE; + goto egress; } /* Don't try to inline functions that are not well-suited to @@ -1410,7 +1520,7 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data) warning ("%Jinlining failed in call to '%F': %s", fn, fn, reason); warning ("called from here"); } - return NULL_TREE; + goto egress; } #ifdef ENABLE_CHECKING @@ -1419,36 +1529,16 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data) #endif if (! lang_hooks.tree_inlining.start_inlining (fn)) - return NULL_TREE; + goto egress; - /* Set the current filename and line number to the function we are - inlining so that when we create new _STMT nodes here they get - line numbers corresponding to the function we are calling. We - wrap the whole inlined body in an EXPR_WITH_FILE_AND_LINE as well - because individual statements don't record the filename. */ - push_srcloc (DECL_SOURCE_FILE (fn), DECL_SOURCE_LINE (fn)); - -#ifndef INLINER_FOR_JAVA - /* Build a statement-expression containing code to initialize the - arguments, the actual inline expansion of the body, and a label - for the return statements within the function to jump to. The - type of the statement expression is the return type of the - function call. */ - expr = build1 (STMT_EXPR, TREE_TYPE (TREE_TYPE (fn)), make_node (COMPOUND_STMT)); - /* There is no scope associated with the statement-expression. */ - STMT_EXPR_NO_SCOPE (expr) = 1; - if (lookup_attribute ("warn_unused_result", - TYPE_ATTRIBUTES (TREE_TYPE (fn)))) - STMT_EXPR_WARN_UNUSED_RESULT (expr) = 1; - stmt = STMT_EXPR_STMT (expr); -#else /* INLINER_FOR_JAVA */ /* Build a block containing code to initialize the arguments, the actual inline expansion of the body, and a label for the return statements within the function to jump to. The type of the statement expression is the return type of the function call. */ stmt = NULL; - expr = build (BLOCK, TREE_TYPE (TREE_TYPE (fn))); -#endif /* INLINER_FOR_JAVA */ + expr = build (BIND_EXPR, TREE_TYPE (TREE_TYPE (fn)), NULL_TREE, + stmt, make_node (BLOCK)); + BLOCK_ABSTRACT_ORIGIN (BIND_EXPR_BLOCK (expr)) = fn; /* Local declarations will be replaced by their equivalents in this map. */ @@ -1463,33 +1553,29 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data) { return_slot_addr = TREE_VALUE (args); args = TREE_CHAIN (args); + TREE_TYPE (expr) = void_type_node; } -#ifndef INLINER_FOR_JAVA - arg_inits = initialize_inlined_parameters (id, args, fn); - /* Expand any inlined calls in the initializers. Do this before we - push FN on the stack of functions we are inlining; we want to - inline calls to FN that appear in the initializers for the - parameters. */ - expand_calls_inline (&arg_inits, id); - /* And add them to the tree. */ - COMPOUND_BODY (stmt) = chainon (COMPOUND_BODY (stmt), arg_inits); -#else /* INLINER_FOR_JAVA */ - arg_inits = initialize_inlined_parameters (id, args, fn, expr); + arg_inits = initialize_inlined_parameters (id, args, TREE_OPERAND (t, 2), + fn, expr); if (arg_inits) { /* Expand any inlined calls in the initializers. Do this before we push FN on the stack of functions we are inlining; we want to inline calls to FN that appear in the initializers for the - parameters. */ + parameters. + + Note we need to save and restore the saved tree statement iterator + to avoid having it clobbered by expand_calls_inline. */ + tree_stmt_iterator save_tsi; + + save_tsi = id->tsi; expand_calls_inline (&arg_inits, id); + id->tsi = save_tsi; /* And add them to the tree. */ - BLOCK_EXPR_BODY (expr) = add_stmt_to_compound (BLOCK_EXPR_BODY (expr), - TREE_TYPE (arg_inits), - arg_inits); + append_to_statement_list (arg_inits, &BIND_EXPR_BODY (expr)); } -#endif /* INLINER_FOR_JAVA */ /* Record the function we are about to inline so that we can avoid recursing into it. */ @@ -1511,45 +1597,18 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data) /* Return statements in the function body will be replaced by jumps to the RET_LABEL. */ id->ret_label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); + DECL_ARTIFICIAL (id->ret_label) = 1; DECL_CONTEXT (id->ret_label) = VARRAY_TREE (id->fns, 0); + insert_decl_map (id, id->ret_label, id->ret_label); if (! DECL_INITIAL (fn) || TREE_CODE (DECL_INITIAL (fn)) != BLOCK) abort (); -#ifndef INLINER_FOR_JAVA - /* Create a block to put the parameters in. We have to do this - after the parameters have been remapped because remapping - parameters is different from remapping ordinary variables. */ - scope_stmt = build_stmt (SCOPE_STMT, DECL_INITIAL (fn)); - SCOPE_BEGIN_P (scope_stmt) = 1; - SCOPE_NO_CLEANUPS_P (scope_stmt) = 1; - remap_block (scope_stmt, DECL_ARGUMENTS (fn), id); - TREE_CHAIN (scope_stmt) = COMPOUND_BODY (stmt); - COMPOUND_BODY (stmt) = scope_stmt; - - /* Tell the debugging backends that this block represents the - outermost scope of the inlined function. */ - if (SCOPE_STMT_BLOCK (scope_stmt)) - BLOCK_ABSTRACT_ORIGIN (SCOPE_STMT_BLOCK (scope_stmt)) = DECL_ORIGIN (fn); - /* Declare the return variable for the function. */ - COMPOUND_BODY (stmt) - = chainon (COMPOUND_BODY (stmt), - declare_return_variable (id, return_slot_addr, &use_stmt)); -#else /* INLINER_FOR_JAVA */ - { - /* Declare the return variable for the function. */ - tree decl = declare_return_variable (id, return_slot_addr, &retvar); - if (retvar) - { - tree *next = &BLOCK_VARS (expr); - while (*next) - next = &TREE_CHAIN (*next); - *next = decl; - } - } -#endif /* INLINER_FOR_JAVA */ + decl = declare_return_variable (id, return_slot_addr, &use_retvar); + if (decl) + declare_inline_vars (expr, decl); /* After we've initialized the parameters, we insert the body of the function itself. */ @@ -1557,67 +1616,25 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data) struct cgraph_node *old_node = id->current_node; id->current_node = edge->callee; -#ifndef INLINER_FOR_JAVA - inlined_body = &COMPOUND_BODY (stmt); - while (*inlined_body) - inlined_body = &TREE_CHAIN (*inlined_body); - *inlined_body = copy_body (id); -#else /* INLINER_FOR_JAVA */ - { - tree new_body; - java_inlining_map_static_initializers (fn, id->decl_map); - new_body = copy_body (id); - TREE_TYPE (new_body) = TREE_TYPE (TREE_TYPE (fn)); - BLOCK_EXPR_BODY (expr) - = add_stmt_to_compound (BLOCK_EXPR_BODY (expr), - TREE_TYPE (new_body), new_body); - inlined_body = &BLOCK_EXPR_BODY (expr); - } -#endif /* INLINER_FOR_JAVA */ + append_to_statement_list (copy_body (id), &BIND_EXPR_BODY (expr)); id->current_node = old_node; } + inlined_body = &BIND_EXPR_BODY (expr); /* After the body of the function comes the RET_LABEL. This must come before we evaluate the returned value below, because that evaluation may cause RTL to be generated. */ -#ifndef INLINER_FOR_JAVA - COMPOUND_BODY (stmt) - = chainon (COMPOUND_BODY (stmt), - build_stmt (LABEL_STMT, id->ret_label)); -#else /* INLINER_FOR_JAVA */ - { - tree label = build1 (LABEL_EXPR, void_type_node, id->ret_label); - BLOCK_EXPR_BODY (expr) - = add_stmt_to_compound (BLOCK_EXPR_BODY (expr), void_type_node, label); - TREE_SIDE_EFFECTS (label) = TREE_SIDE_EFFECTS (t); - } -#endif /* INLINER_FOR_JAVA */ - - /* Finally, mention the returned value so that the value of the - statement-expression is the returned value of the function. */ -#ifndef INLINER_FOR_JAVA - COMPOUND_BODY (stmt) = chainon (COMPOUND_BODY (stmt), use_stmt); - - /* Close the block for the parameters. */ - scope_stmt = build_stmt (SCOPE_STMT, DECL_INITIAL (fn)); - SCOPE_NO_CLEANUPS_P (scope_stmt) = 1; - remap_block (scope_stmt, NULL_TREE, id); - COMPOUND_BODY (stmt) - = chainon (COMPOUND_BODY (stmt), scope_stmt); -#else /* INLINER_FOR_JAVA */ - if (retvar) + if (TREE_USED (id->ret_label)) { - /* Mention the retvar. If the return type of the function was - promoted, convert it back to the expected type. */ - if (TREE_TYPE (TREE_TYPE (fn)) != TREE_TYPE (retvar)) - retvar = build1 (NOP_EXPR, TREE_TYPE (TREE_TYPE (fn)), retvar); - BLOCK_EXPR_BODY (expr) - = add_stmt_to_compound (BLOCK_EXPR_BODY (expr), - TREE_TYPE (retvar), retvar); + tree label = build1 (LABEL_EXPR, void_type_node, id->ret_label); + append_to_statement_list (label, &BIND_EXPR_BODY (expr)); } - java_inlining_merge_static_initializers (fn, id->decl_map); -#endif /* INLINER_FOR_JAVA */ + /* Finally, mention the returned value so that the value of the + statement-expression is the returned value of the function. */ + if (use_retvar) + /* Set TREE_TYPE on BIND_EXPR? */ + append_to_statement_list_force (use_retvar, &BIND_EXPR_BODY (expr)); /* Clean up. */ splay_tree_delete (id->decl_map); @@ -1626,19 +1643,54 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data) /* The new expression has side-effects if the old one did. */ TREE_SIDE_EFFECTS (expr) = TREE_SIDE_EFFECTS (t); - /* Replace the call by the inlined body. Wrap it in an - EXPR_WITH_FILE_LOCATION so that we'll get debugging line notes - pointing to the right place. */ -#ifndef INLINER_FOR_JAVA - chain = TREE_CHAIN (*tp); -#endif /* INLINER_FOR_JAVA */ - *tp = build_expr_wfl (expr, DECL_SOURCE_FILE (fn), DECL_SOURCE_LINE (fn), - /*col=*/0); - EXPR_WFL_EMIT_LINE_NOTE (*tp) = 1; -#ifndef INLINER_FOR_JAVA - TREE_CHAIN (*tp) = chain; -#endif /* not INLINER_FOR_JAVA */ - pop_srcloc (); + /* If we are working with gimple form, then we need to keep the tree + in gimple form. If we are not in gimple form, we can just replace + *tp with the new BIND_EXPR. */ + if (lang_hooks.gimple_before_inlining) + { + tree save_decl; + + /* Keep the new trees in gimple form. */ + BIND_EXPR_BODY (expr) + = rationalize_compound_expr (BIND_EXPR_BODY (expr)); + + /* We want to create a new variable to hold the result of the + inlined body. This new variable needs to be added to the + function which we are inlining into, thus the saving and + restoring of current_function_decl. */ + save_decl = current_function_decl; + current_function_decl = id->node->decl; + inline_result = voidify_wrapper_expr (expr); + current_function_decl = save_decl; + + /* If the inlined function returns a result that we care about, + then we're going to need to splice in a MODIFY_EXPR. Otherwise + the call was a standalone statement and we can just replace it + with the BIND_EXPR inline representation of the called function. */ + if (TREE_CODE (tsi_stmt (id->tsi)) != CALL_EXPR) + { + tsi_link_before (&id->tsi, expr, TSI_SAME_STMT); + *tp = inline_result; + } + else + *tp = expr; + + /* When we gimplify a function call, we may clear TREE_SIDE_EFFECTS + on the call if it is to a "const" function. Thus the copy of + TREE_SIDE_EFFECTS from the CALL_EXPR to the BIND_EXPR above + with result in TREE_SIDE_EFFECTS not being set for the inlined + copy of a "const" function. + + Unfortunately, that is wrong as inlining the function + can create/expose interesting side effects (such as setting + of a return value). + + The easiest solution is to simply recalculate TREE_SIDE_EFFECTS + for the toplevel expression. */ + recalculate_side_effects (expr); + } + else + *tp = expr; /* If the value of the new expression is ignored, that's OK. We don't warn about this for CALL_EXPRs, so we shouldn't warn about @@ -1658,21 +1710,108 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data) lang_hooks.tree_inlining.end_inlining (fn); /* Keep iterating. */ + egress: + input_location = saved_location; return NULL_TREE; } + +static void +gimple_expand_calls_inline (tree *stmt_p, inline_data *id) +{ + tree stmt = *stmt_p; + enum tree_code code = TREE_CODE (stmt); + int dummy; + + switch (code) + { + case STATEMENT_LIST: + { + tree_stmt_iterator i; + tree new; + + for (i = tsi_start (stmt); !tsi_end_p (i); ) + { + id->tsi = i; + gimple_expand_calls_inline (tsi_stmt_ptr (i), id); + + new = tsi_stmt (i); + if (TREE_CODE (new) == STATEMENT_LIST) + { + tsi_link_before (&i, new, TSI_SAME_STMT); + tsi_delink (&i); + } + else + tsi_next (&i); + } + } + break; + + case COND_EXPR: + gimple_expand_calls_inline (&COND_EXPR_THEN (stmt), id); + gimple_expand_calls_inline (&COND_EXPR_ELSE (stmt), id); + break; + case CATCH_EXPR: + gimple_expand_calls_inline (&CATCH_BODY (stmt), id); + break; + case EH_FILTER_EXPR: + gimple_expand_calls_inline (&EH_FILTER_FAILURE (stmt), id); + break; + case TRY_CATCH_EXPR: + case TRY_FINALLY_EXPR: + gimple_expand_calls_inline (&TREE_OPERAND (stmt, 0), id); + gimple_expand_calls_inline (&TREE_OPERAND (stmt, 1), id); + break; + case BIND_EXPR: + gimple_expand_calls_inline (&BIND_EXPR_BODY (stmt), id); + break; + + case COMPOUND_EXPR: + /* We're gimple. We should have gotten rid of all these. */ + abort (); + + case RETURN_EXPR: + stmt_p = &TREE_OPERAND (stmt, 0); + stmt = *stmt_p; + if (!stmt || TREE_CODE (stmt) != MODIFY_EXPR) + break; + /* FALLTHRU */ + case MODIFY_EXPR: + stmt_p = &TREE_OPERAND (stmt, 1); + stmt = *stmt_p; + if (TREE_CODE (stmt) != CALL_EXPR) + break; + /* FALLTHRU */ + case CALL_EXPR: + expand_call_inline (stmt_p, &dummy, id); + break; + + default: + break; + } +} + /* Walk over the entire tree *TP, replacing CALL_EXPRs with inline expansions as appropriate. */ static void expand_calls_inline (tree *tp, inline_data *id) { - /* Search through *TP, replacing all calls to inline functions by - appropriate equivalents. Use walk_tree in no-duplicates mode - to avoid exponential time complexity. (We can't just use - walk_tree_without_duplicates, because of the special TARGET_EXPR - handling in expand_calls. The hash table is set up in - optimize_function. */ - walk_tree (tp, expand_call_inline, id, id->tree_pruner); + /* If we are not in gimple form, then we want to walk the tree + recursively as we do not know anything about the structure + of the tree. */ + + if (!lang_hooks.gimple_before_inlining) + { + walk_tree (tp, expand_call_inline, id, id->tree_pruner); + return; + } + + /* We are in gimple form. We want to stay in gimple form. Walk + the statements, inlining calls in each statement. By walking + the statements, we have enough information to keep the tree + in gimple form as we insert inline bodies. */ + + gimple_expand_calls_inline (tp, id); } /* Expand calls to inline functions in the body of FN. */ @@ -1731,6 +1870,7 @@ optimize_inline_calls (tree fn) VARRAY_ACTIVE_SIZE (id.inlined_fns) * sizeof (tree)); DECL_INLINED_FNS (fn) = ifn; } + #ifdef ENABLE_CHECKING { struct cgraph_edge *e; @@ -1800,8 +1940,6 @@ save_body (tree fn, tree *arg_copy) /* Actually copy the body. */ body = copy_body (&id); - if (lang_hooks.update_decl_after_saving) - lang_hooks.update_decl_after_saving (fn, id.decl_map); /* Clean up. */ splay_tree_delete (id.decl_map); @@ -1867,12 +2005,11 @@ walk_tree (tree *tp, walk_tree_fn func, void *data, void *htab_) code = TREE_CODE (*tp); -#ifndef INLINER_FOR_JAVA /* Even if we didn't, FUNC may have decided that there was nothing interesting below this point in the tree. */ if (!walk_subtrees) { - if (STATEMENT_CODE_P (code) || code == TREE_LIST + if (code == TREE_LIST || lang_hooks.tree_inlining.tree_chain_matters_p (*tp)) /* But we still need to check our siblings. */ WALK_SUBTREE_TAIL (TREE_CHAIN (*tp)); @@ -1880,23 +2017,18 @@ walk_tree (tree *tp, walk_tree_fn func, void *data, void *htab_) return NULL_TREE; } - /* Handle common cases up front. */ - if (IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (code))) -#else /* INLINER_FOR_JAVA */ + result = (*lang_hooks.tree_inlining.walk_subtrees) (tp, &walk_subtrees, func, + data, htab); + if (result || ! walk_subtrees) + return result; + if (code != EXIT_BLOCK_EXPR && code != SAVE_EXPR + && code != BIND_EXPR && IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (code))) -#endif /* INLINER_FOR_JAVA */ { int i, len; -#ifndef INLINER_FOR_JAVA - /* Set lineno here so we get the right instantiation context - if we call instantiate_decl from inlinable_function_p. */ - if (STATEMENT_CODE_P (code) && !STMT_LINENO_FOR_FN_P (*tp)) - input_line = STMT_LINENO (*tp); -#endif /* not INLINER_FOR_JAVA */ - /* Walk over all the sub-trees of this operand. */ len = first_rtl_op (code); /* TARGET_EXPRs are peculiar: operands 1 and 3 can be the same. @@ -1906,147 +2038,161 @@ walk_tree (tree *tp, walk_tree_fn func, void *data, void *htab_) --len; /* Go through the subtrees. We need to do this in forward order so that the scope of a FOR_EXPR is handled properly. */ +#ifdef DEBUG_WALK_TREE for (i = 0; i < len; ++i) WALK_SUBTREE (TREE_OPERAND (*tp, i)); +#else + for (i = 0; i < len - 1; ++i) + WALK_SUBTREE (TREE_OPERAND (*tp, i)); -#ifndef INLINER_FOR_JAVA - /* For statements, we also walk the chain so that we cover the - entire statement tree. */ - if (STATEMENT_CODE_P (code)) + if (len) { - if (code == DECL_STMT - && DECL_STMT_DECL (*tp) - && DECL_P (DECL_STMT_DECL (*tp))) - { - /* Walk the DECL_INITIAL and DECL_SIZE. We don't want to walk - into declarations that are just mentioned, rather than - declared; they don't really belong to this part of the tree. - And, we can see cycles: the initializer for a declaration can - refer to the declaration itself. */ - WALK_SUBTREE (DECL_INITIAL (DECL_STMT_DECL (*tp))); - WALK_SUBTREE (DECL_SIZE (DECL_STMT_DECL (*tp))); - WALK_SUBTREE (DECL_SIZE_UNIT (DECL_STMT_DECL (*tp))); - WALK_SUBTREE (TREE_TYPE (*tp)); - } - - /* This can be tail-recursion optimized if we write it this way. */ - WALK_SUBTREE_TAIL (TREE_CHAIN (*tp)); + /* The common case is that we may tail recurse here. */ + if (code != BIND_EXPR + && !TREE_CHAIN (*tp)) + WALK_SUBTREE_TAIL (TREE_OPERAND (*tp, len - 1)); + else + WALK_SUBTREE (TREE_OPERAND (*tp, len - 1)); } +#endif -#endif /* not INLINER_FOR_JAVA */ - /* We didn't find what we were looking for. */ - return NULL_TREE; + if ((*lang_hooks.tree_inlining.tree_chain_matters_p) (*tp)) + /* Check our siblings. */ + WALK_SUBTREE_TAIL (TREE_CHAIN (*tp)); } else if (TREE_CODE_CLASS (code) == 'd') { WALK_SUBTREE_TAIL (TREE_TYPE (*tp)); } - else if (TREE_CODE_CLASS (code) == 't') + else { - WALK_SUBTREE (TYPE_SIZE (*tp)); - WALK_SUBTREE (TYPE_SIZE_UNIT (*tp)); - /* Also examine various special fields, below. */ - } + if (TREE_CODE_CLASS (code) == 't') + { + WALK_SUBTREE (TYPE_SIZE (*tp)); + WALK_SUBTREE (TYPE_SIZE_UNIT (*tp)); + /* Also examine various special fields, below. */ + } - result = lang_hooks.tree_inlining.walk_subtrees (tp, &walk_subtrees, func, - data, htab); - if (result || ! walk_subtrees) - return result; + /* Not one of the easy cases. We must explicitly go through the + children. */ + switch (code) + { + case ERROR_MARK: + case IDENTIFIER_NODE: + case INTEGER_CST: + case REAL_CST: + case VECTOR_CST: + case STRING_CST: + case REAL_TYPE: + case COMPLEX_TYPE: + case VECTOR_TYPE: + case VOID_TYPE: + case BOOLEAN_TYPE: + case UNION_TYPE: + case ENUMERAL_TYPE: + case BLOCK: + case RECORD_TYPE: + case PLACEHOLDER_EXPR: + case SSA_NAME: + /* None of thse have subtrees other than those already walked + above. */ + break; - /* Not one of the easy cases. We must explicitly go through the - children. */ - switch (code) - { - case ERROR_MARK: - case IDENTIFIER_NODE: - case INTEGER_CST: - case REAL_CST: - case VECTOR_CST: - case STRING_CST: - case REAL_TYPE: - case COMPLEX_TYPE: - case VECTOR_TYPE: - case VOID_TYPE: - case BOOLEAN_TYPE: - case UNION_TYPE: - case ENUMERAL_TYPE: - case BLOCK: - case RECORD_TYPE: - case CHAR_TYPE: - case PLACEHOLDER_EXPR: - /* None of these have subtrees other than those already walked - above. */ - break; + case POINTER_TYPE: + case REFERENCE_TYPE: + WALK_SUBTREE_TAIL (TREE_TYPE (*tp)); + break; - case POINTER_TYPE: - case REFERENCE_TYPE: - WALK_SUBTREE_TAIL (TREE_TYPE (*tp)); - break; + case TREE_LIST: + WALK_SUBTREE (TREE_VALUE (*tp)); + WALK_SUBTREE_TAIL (TREE_CHAIN (*tp)); + break; - case TREE_LIST: - WALK_SUBTREE (TREE_VALUE (*tp)); - WALK_SUBTREE_TAIL (TREE_CHAIN (*tp)); - break; + case TREE_VEC: + { + int len = TREE_VEC_LENGTH (*tp); - case TREE_VEC: - { - int len = TREE_VEC_LENGTH (*tp); + if (len == 0) + break; - if (len == 0) - break; + /* Walk all elements but the first. */ + while (--len) + WALK_SUBTREE (TREE_VEC_ELT (*tp, len)); - /* Walk all elements but the first. */ - while (--len) - WALK_SUBTREE (TREE_VEC_ELT (*tp, len)); + /* Now walk the first one as a tail call. */ + WALK_SUBTREE_TAIL (TREE_VEC_ELT (*tp, 0)); + } - /* Now walk the first one as a tail call. */ - WALK_SUBTREE_TAIL (TREE_VEC_ELT (*tp, 0)); - } + case COMPLEX_CST: + WALK_SUBTREE (TREE_REALPART (*tp)); + WALK_SUBTREE_TAIL (TREE_IMAGPART (*tp)); - case COMPLEX_CST: - WALK_SUBTREE (TREE_REALPART (*tp)); - WALK_SUBTREE_TAIL (TREE_IMAGPART (*tp)); + case CONSTRUCTOR: + WALK_SUBTREE_TAIL (CONSTRUCTOR_ELTS (*tp)); - case CONSTRUCTOR: - WALK_SUBTREE_TAIL (CONSTRUCTOR_ELTS (*tp)); + case METHOD_TYPE: + WALK_SUBTREE (TYPE_METHOD_BASETYPE (*tp)); + /* Fall through. */ - case METHOD_TYPE: - WALK_SUBTREE (TYPE_METHOD_BASETYPE (*tp)); - /* Fall through. */ + case FUNCTION_TYPE: + WALK_SUBTREE (TREE_TYPE (*tp)); + { + tree arg = TYPE_ARG_TYPES (*tp); - case FUNCTION_TYPE: - WALK_SUBTREE (TREE_TYPE (*tp)); - { - tree arg = TYPE_ARG_TYPES (*tp); + /* We never want to walk into default arguments. */ + for (; arg; arg = TREE_CHAIN (arg)) + WALK_SUBTREE (TREE_VALUE (arg)); + } + break; - /* We never want to walk into default arguments. */ - for (; arg; arg = TREE_CHAIN (arg)) - WALK_SUBTREE (TREE_VALUE (arg)); - } - break; + case ARRAY_TYPE: + WALK_SUBTREE (TREE_TYPE (*tp)); + WALK_SUBTREE_TAIL (TYPE_DOMAIN (*tp)); - case ARRAY_TYPE: - WALK_SUBTREE (TREE_TYPE (*tp)); - WALK_SUBTREE_TAIL (TYPE_DOMAIN (*tp)); + case INTEGER_TYPE: + case CHAR_TYPE: + WALK_SUBTREE (TYPE_MIN_VALUE (*tp)); + WALK_SUBTREE_TAIL (TYPE_MAX_VALUE (*tp)); - case INTEGER_TYPE: - WALK_SUBTREE (TYPE_MIN_VALUE (*tp)); - WALK_SUBTREE_TAIL (TYPE_MAX_VALUE (*tp)); + case OFFSET_TYPE: + WALK_SUBTREE (TREE_TYPE (*tp)); + WALK_SUBTREE_TAIL (TYPE_OFFSET_BASETYPE (*tp)); - case OFFSET_TYPE: - WALK_SUBTREE (TREE_TYPE (*tp)); - WALK_SUBTREE_TAIL (TYPE_OFFSET_BASETYPE (*tp)); + case EXIT_BLOCK_EXPR: + WALK_SUBTREE_TAIL (TREE_OPERAND (*tp, 1)); -#ifdef INLINER_FOR_JAVA - case EXIT_BLOCK_EXPR: - WALK_SUBTREE_TAIL (TREE_OPERAND (*tp, 1)); + case SAVE_EXPR: + WALK_SUBTREE_TAIL (TREE_OPERAND (*tp, 0)); - case SAVE_EXPR: - WALK_SUBTREE_TAIL (TREE_OPERAND (*tp, 0)); -#endif /* INLINER_FOR_JAVA */ + case BIND_EXPR: + { + tree decl; + for (decl = BIND_EXPR_VARS (*tp); decl; decl = TREE_CHAIN (decl)) + { + /* Walk the DECL_INITIAL and DECL_SIZE. We don't want to walk + into declarations that are just mentioned, rather than + declared; they don't really belong to this part of the tree. + And, we can see cycles: the initializer for a declaration can + refer to the declaration itself. */ + WALK_SUBTREE (DECL_INITIAL (decl)); + WALK_SUBTREE (DECL_SIZE (decl)); + WALK_SUBTREE (DECL_SIZE_UNIT (decl)); + WALK_SUBTREE (TREE_TYPE (decl)); + } + WALK_SUBTREE_TAIL (BIND_EXPR_BODY (*tp)); + } - default: - abort (); + case STATEMENT_LIST: + { + tree_stmt_iterator i; + for (i = tsi_start (*tp); !tsi_end_p (i); tsi_next (&i)) + WALK_SUBTREE (*tsi_stmt_ptr (i)); + } + break; + + default: + abort (); + } } /* We didn't find what we were looking for. */ @@ -2089,29 +2235,34 @@ copy_tree_r (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED) /* Because the chain gets clobbered when we make a copy, we save it here. */ tree chain = TREE_CHAIN (*tp); + tree new; /* Copy the node. */ - *tp = copy_node (*tp); + new = copy_node (*tp); + + /* Propagate mudflap marked-ness. */ + if (flag_mudflap && mf_marked_p (*tp)) + mf_mark (new); + + *tp = new; /* Now, restore the chain, if appropriate. That will cause walk_tree to walk into the chain as well. */ if (code == PARM_DECL || code == TREE_LIST -#ifndef INLINER_FOR_JAVA - || lang_hooks.tree_inlining.tree_chain_matters_p (*tp) - || STATEMENT_CODE_P (code)) + || lang_hooks.tree_inlining.tree_chain_matters_p (*tp)) TREE_CHAIN (*tp) = chain; /* For now, we don't update BLOCKs when we make copies. So, we - have to nullify all scope-statements. */ - if (TREE_CODE (*tp) == SCOPE_STMT) - SCOPE_STMT_BLOCK (*tp) = NULL_TREE; -#else /* INLINER_FOR_JAVA */ - || lang_hooks.tree_inlining.tree_chain_matters_p (*tp)) - TREE_CHAIN (*tp) = chain; -#endif /* INLINER_FOR_JAVA */ + have to nullify all BIND_EXPRs. */ + if (TREE_CODE (*tp) == BIND_EXPR) + BIND_EXPR_BLOCK (*tp) = NULL_TREE; } else if (TREE_CODE_CLASS (code) == 't') *walk_subtrees = 0; + else if (TREE_CODE_CLASS (code) == 'd') + *walk_subtrees = 0; + else if (code == STATEMENT_LIST) + abort (); return NULL_TREE; } @@ -2157,19 +2308,148 @@ remap_save_expr (tree *tp, void *st_, tree fn, int *walk_subtrees) *tp = t; } -#ifdef INLINER_FOR_JAVA -/* Add STMT to EXISTING if possible, otherwise create a new - COMPOUND_EXPR and add STMT to it. */ +/* Called via walk_tree. If *TP points to a DECL_STMT for a local + declaration, copies the declaration and enters it in the splay_tree + in DATA (which is really an `inline_data *'). */ + +static tree +mark_local_for_remap_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED, + void *data) +{ + tree t = *tp; + inline_data *id = (inline_data *) data; + tree decl; + + /* Don't walk into types. */ + if (TYPE_P (t)) + { + *walk_subtrees = 0; + return NULL_TREE; + } + + if (TREE_CODE (t) == LABEL_EXPR) + decl = TREE_OPERAND (t, 0); + else + /* We don't need to handle anything else ahead of time. */ + decl = NULL_TREE; + + if (decl) + { + tree copy; + + /* Make a copy. */ + copy = copy_decl_for_inlining (decl, + DECL_CONTEXT (decl), + DECL_CONTEXT (decl)); + + /* Remember the copy. */ + insert_decl_map (id, decl, copy); + } + + return NULL_TREE; +} + +/* Called via walk_tree when an expression is unsaved. Using the + splay_tree pointed to by ST (which is really a `splay_tree'), + remaps all local declarations to appropriate replacements. */ static tree -add_stmt_to_compound (tree existing, tree type, tree stmt) +unsave_r (tree *tp, int *walk_subtrees, void *data) { - if (!stmt) - return existing; - else if (existing) - return build (COMPOUND_EXPR, type, existing, stmt); + inline_data *id = (inline_data *) data; + splay_tree st = id->decl_map; + splay_tree_node n; + + /* Only a local declaration (variable or label). */ + if ((TREE_CODE (*tp) == VAR_DECL && !TREE_STATIC (*tp)) + || TREE_CODE (*tp) == LABEL_DECL) + { + /* Lookup the declaration. */ + n = splay_tree_lookup (st, (splay_tree_key) *tp); + + /* If it's there, remap it. */ + if (n) + *tp = (tree) n->value; + } + else if (TREE_CODE (*tp) == STATEMENT_LIST) + copy_statement_list (tp); + else if (TREE_CODE (*tp) == BIND_EXPR) + copy_bind_expr (tp, walk_subtrees, id); + else if (TREE_CODE (*tp) == SAVE_EXPR) + remap_save_expr (tp, st, current_function_decl, walk_subtrees); else - return stmt; + { + copy_tree_r (tp, walk_subtrees, NULL); + + /* Do whatever unsaving is required. */ + unsave_expr_1 (*tp); + } + + /* Keep iterating. */ + return NULL_TREE; } -#endif /* INLINER_FOR_JAVA */ +/* Default lang hook for "unsave_expr_now". Copies everything in EXPR and + replaces variables, labels and SAVE_EXPRs local to EXPR. */ + +tree +lhd_unsave_expr_now (tree expr) +{ + inline_data id; + + /* There's nothing to do for NULL_TREE. */ + if (expr == 0) + return expr; + + /* Set up ID. */ + memset (&id, 0, sizeof (id)); + VARRAY_TREE_INIT (id.fns, 1, "fns"); + VARRAY_PUSH_TREE (id.fns, current_function_decl); + id.decl_map = splay_tree_new (splay_tree_compare_pointers, NULL, NULL); + + /* Walk the tree once to find local labels. */ + walk_tree_without_duplicates (&expr, mark_local_for_remap_r, &id); + + /* Walk the tree again, copying, remapping, and unsaving. */ + walk_tree (&expr, unsave_r, &id, NULL); + + /* Clean up. */ + splay_tree_delete (id.decl_map); + + return expr; +} + +/* Allow someone to determine if SEARCH is a child of TOP from gdb. */ +static tree +debug_find_tree_1 (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED, void *data) +{ + if (*tp == data) + return (tree) data; + else + return NULL; +} + +extern bool debug_find_tree (tree top, tree search); + +bool +debug_find_tree (tree top, tree search) +{ + return walk_tree_without_duplicates (&top, debug_find_tree_1, search) != 0; +} + + +/* Declare the variables created by the inliner. Add all the variables in + VARS to BIND_EXPR. */ + +static void +declare_inline_vars (tree bind_expr, tree vars) +{ + if (lang_hooks.gimple_before_inlining) + { + tree t; + for (t = vars; t; t = TREE_CHAIN (t)) + vars->decl.seen_in_bind_expr = 1; + } + + add_var_to_bind_expr (bind_expr, vars); +} |