X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Ftree-inline.c;h=270389bc26567f74eeb8140e06622d1481490444;hb=c9d8753c393285ea9685bcdc894e4b7588d2a577;hp=db2b2361874cbab83259787f640bd0a929fd7537;hpb=78a289a921a5787579715c5d70fcd75c6f072a1b;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c index db2b2361874..270389bc265 100644 --- a/gcc/tree-inline.c +++ b/gcc/tree-inline.c @@ -1,5 +1,5 @@ /* Tree inlining. - Copyright 2001, 2002, 2003, 2004 Free Software Foundation, Inc. + Copyright 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. Contributed by Alexandre Oliva This file is part of GCC. @@ -16,8 +16,8 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING. If not, write to -the Free Software Foundation, 59 Temple Place - Suite 330, -Boston, MA 02111-1307, USA. */ +the Free Software Foundation, 51 Franklin Street, Fifth Floor, +Boston, MA 02110-1301, USA. */ #include "config.h" #include "system.h" @@ -32,23 +32,58 @@ Boston, MA 02111-1307, USA. */ #include "params.h" #include "input.h" #include "insn-config.h" -#include "integrate.h" #include "varray.h" #include "hashtab.h" -#include "pointer-set.h" #include "splay-tree.h" #include "langhooks.h" +#include "basic-block.h" +#include "tree-iterator.h" #include "cgraph.h" #include "intl.h" #include "tree-mudflap.h" +#include "tree-flow.h" #include "function.h" +#include "ggc.h" +#include "tree-flow.h" #include "diagnostic.h" +#include "except.h" +#include "debug.h" +#include "pointer-set.h" +#include "ipa-prop.h" /* I'm not real happy about this, but we need to handle gimple and non-gimple trees. */ -#include "tree-iterator.h" #include "tree-gimple.h" +/* Inlining, Saving, Cloning + + Inlining: a function body is duplicated, but the PARM_DECLs are + remapped into VAR_DECLs, and non-void RETURN_EXPRs become + MODIFY_EXPRs that store to a dedicated returned-value variable. + The duplicated eh_region info of the copy will later be appended + to the info for the caller; the eh_region info in copied throwing + statements and RESX_EXPRs is adjusted accordingly. + + Saving: make a semantically-identical copy of the function body. + Necessary when we want to generate code for the body (a destructive + operation), but we expect to need this body in the future (e.g. for + inlining into another function). + + Cloning: (only in C++) We have one body for a con/de/structor, and + multiple function decls, each with a unique parameter list. + Duplicate the body, using the given splay tree; some parameters + will become constants (like 0 or 1). + + All of these will simultaneously lookup any callgraph edges. If + we're going to inline the duplicated function body, and the given + function has some cloned callgraph nodes (one for each place this + function will be inlined) those callgraph edges will be duplicated. + If we're saving or cloning the body, those callgraph edges will be + updated to point into the new body. (Note that the original + callgraph node and edge list will not be altered.) + + See the CALL_EXPR handling case in copy_body_r (). */ + /* 0 if we should not perform inlining. 1 if we should expand functions calls inline at the tree level. 2 if we should consider *all* functions to be inline @@ -72,30 +107,19 @@ int flag_inline_trees = 0; typedef struct inline_data { - /* A stack of the functions we are inlining. For example, if we are - compiling `f', which calls `g', which calls `h', and we are - inlining the body of `h', the stack will contain, `h', followed - by `g', followed by `f'. The first few elements of the stack may - contain other functions that we know we should not recurse into, - even though they are not directly being inlined. */ - varray_type fns; - /* The index of the first element of FNS that really represents an - inlined function. */ - unsigned first_inlined_fn; - /* The label to jump to when a return statement is encountered. If - this value is NULL, then return statements will simply be - remapped as return statements, rather than as jumps. */ - tree ret_label; + /* FUNCTION_DECL for function being inlined. */ + tree callee; + /* FUNCTION_DECL for function being inlined into. */ + tree caller; + /* struct function for function being inlined. Usually this is the same + as DECL_STRUCT_FUNCTION (callee), but can be different if saved_cfg + and saved_eh are in use. */ + struct function *callee_cfun; /* 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; - /* Nonzero if we are currently within the cleanup for a - TARGET_EXPR. */ - int in_target_cleanup_p; - /* A list of the functions current function has inlined. */ - varray_type inlined_fns; /* We use the same mechanism to build clones that we do to perform inlining. However, there are a few places where we need to distinguish between those two situations. This flag is true if @@ -103,43 +127,42 @@ typedef struct inline_data bool cloning_p; /* Similarly for saving function body. */ bool saving_p; - /* Hash table used to prevent walk_tree from visiting the same node - umpteen million times. */ - htab_t tree_pruner; + /* Versioning function is slightly different from inlining. */ + bool versioning_p; /* Callgraph node of function we are inlining into. */ 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; + /* Current BLOCK. */ + tree block; + varray_type ipa_info; + /* Exception region the inlined call lie in. */ + int eh_region; + /* Take region number in the function being copied, add this value and + get eh region number of the duplicate in the function we inline into. */ + int eh_region_offset; } 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, tree *); static tree copy_body_r (tree *, int *, void *); -static tree copy_body (inline_data *); -static tree expand_call_inline (tree *, int *, void *); -static void expand_calls_inline (tree *, inline_data *); +static tree copy_generic_body (inline_data *); static bool inlinable_function_p (tree); static tree remap_decl (tree, inline_data *); static tree remap_type (tree, inline_data *); -static tree initialize_inlined_parameters (inline_data *, tree, - tree, tree, tree); static void remap_block (tree *, inline_data *); +static tree remap_decl (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 void unsave_expr_1 (tree); static tree unsave_r (tree *, int *, void *); -static void declare_inline_vars (tree bind_expr, tree vars); +static void declare_inline_vars (tree, tree); static void remap_save_expr (tree *, void *, int *); +static bool replace_ref_tree (inline_data *, tree *); +static inline bool inlining_p (inline_data *); +static void add_lexical_block (tree current_block, tree new_block); /* Insert a tree->tree mapping for ID. Despite the name suggests that the trees should be variables, it is used for more than that. */ @@ -157,29 +180,38 @@ insert_decl_map (inline_data *id, tree key, tree value) (splay_tree_value) value); } -/* Remap DECL during the copying of the BLOCK tree for the function. - We are only called to remap local variables in the current function. */ +/* Remap DECL during the copying of the BLOCK tree for the function. */ static tree remap_decl (tree decl, inline_data *id) { - splay_tree_node n = splay_tree_lookup (id->decl_map, (splay_tree_key) decl); - tree fn = VARRAY_TOP_TREE (id->fns); + splay_tree_node n; + tree fn; + + /* We only remap local variables in the current function. */ + fn = id->callee; + + /* See if we have remapped this declaration. */ - /* See if we have remapped this declaration. If we didn't already have an - equivalent for this declaration, create one now. */ + n = splay_tree_lookup (id->decl_map, (splay_tree_key) decl); + + /* If we didn't already have an equivalent for this declaration, + create one now. */ if (!n) { /* Make a copy of the variable or label. */ - tree t = copy_decl_for_inlining (decl, fn, VARRAY_TREE (id->fns, 0)); + tree t; + t = copy_decl_for_dup (decl, fn, id->caller, id->versioning_p); + + /* Remember it, so that if we encounter this local entity again + we can reuse this copy. Do this early because remap_type may + need this decl for TYPE_STUB_DECL. */ + insert_decl_map (id, decl, t); /* Remap types, if necessary. */ TREE_TYPE (t) = remap_type (TREE_TYPE (t), id); if (TREE_CODE (t) == TYPE_DECL) DECL_ORIGINAL_TYPE (t) = remap_type (DECL_ORIGINAL_TYPE (t), id); - else if (TREE_CODE (t) == PARM_DECL) - DECL_ARG_TYPE_AS_WRITTEN (t) - = remap_type (DECL_ARG_TYPE_AS_WRITTEN (t), id); /* Remap sizes as necessary. */ walk_tree (&DECL_SIZE (t), copy_body_r, id, NULL); @@ -225,27 +257,10 @@ remap_decl (tree decl, inline_data *id) } static tree -remap_type (tree type, inline_data *id) +remap_type_1 (tree type, inline_data *id) { - splay_tree_node node; tree new, t; - if (type == NULL) - return type; - - /* See if we have remapped this type. */ - node = splay_tree_lookup (id->decl_map, (splay_tree_key) type); - if (node) - return (tree) node->value; - - /* The type only needs remapping if it's variably modified by a variable - in the function we are inlining. */ - if (! variably_modified_type_p (type, VARRAY_TOP_TREE (id->fns))) - { - insert_decl_map (id, type, type); - return type; - } - /* We do need a copy. build and register it now. If this is a pointer or reference type, remap the designated type and make a new pointer or reference type. */ @@ -286,6 +301,9 @@ remap_type (tree type, inline_data *id) TYPE_NEXT_VARIANT (new) = NULL; } + if (TYPE_STUB_DECL (type)) + TYPE_STUB_DECL (new) = remap_decl (TYPE_STUB_DECL (type), id); + /* Lazily create pointer and reference types. */ TYPE_POINTER_TO (new) = NULL; TYPE_REFERENCE_TO (new) = NULL; @@ -319,10 +337,20 @@ remap_type (tree type, inline_data *id) case RECORD_TYPE: case UNION_TYPE: case QUAL_UNION_TYPE: - walk_tree (&TYPE_FIELDS (new), copy_body_r, id, NULL); + { + tree f, nf = NULL; + + for (f = TYPE_FIELDS (new); f ; f = TREE_CHAIN (f)) + { + t = remap_decl (f, id); + DECL_CONTEXT (t) = new; + TREE_CHAIN (t) = nf; + nf = t; + } + TYPE_FIELDS (new) = nreverse (nf); + } break; - case FILE_TYPE: case OFFSET_TYPE: default: /* Shouldn't have been thought variable sized. */ @@ -336,6 +364,29 @@ remap_type (tree type, inline_data *id) } static tree +remap_type (tree type, inline_data *id) +{ + splay_tree_node node; + + if (type == NULL) + return type; + + /* See if we have remapped this type. */ + node = splay_tree_lookup (id->decl_map, (splay_tree_key) type); + if (node) + return (tree) node->value; + + /* The type only needs remapping if it's variably modified. */ + if (! variably_modified_type_p (type, id->callee)) + { + insert_decl_map (id, type, type); + return type; + } + + return remap_type_1 (type, id); +} + +static tree remap_decls (tree decls, inline_data *id) { tree old_var; @@ -346,6 +397,17 @@ remap_decls (tree decls, inline_data *id) { tree new_var; + /* We can not chain the local static declarations into the unexpanded_var_list + as we can't duplicate them or break one decl rule. Go ahead and link + them into unexpanded_var_list. */ + if (!lang_hooks.tree_inlining.auto_var_in_fn_p (old_var, id->callee) + && !DECL_EXTERNAL (old_var)) + { + cfun->unexpanded_var_list = tree_cons (NULL_TREE, old_var, + cfun->unexpanded_var_list); + continue; + } + /* Remap the variable. */ new_var = remap_decl (old_var, id); @@ -380,38 +442,39 @@ remap_block (tree *block, inline_data *id) new_block = make_node (BLOCK); TREE_USED (new_block) = TREE_USED (old_block); BLOCK_ABSTRACT_ORIGIN (new_block) = old_block; + BLOCK_SOURCE_LOCATION (new_block) = BLOCK_SOURCE_LOCATION (old_block); *block = new_block; /* Remap its variables. */ BLOCK_VARS (new_block) = remap_decls (BLOCK_VARS (old_block), id); - 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. */ + fn = id->caller; 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 - first_block = &DECL_INITIAL (fn); - BLOCK_CHAIN (new_block) = *first_block; - *first_block = new_block; - } -#endif /* Remember the remapped block. */ insert_decl_map (id, old_block, new_block); } +/* Copy the whole block tree and root it in id->block. */ +static tree +remap_blocks (tree block, inline_data *id) +{ + tree t; + tree new = block; + + if (!block) + return NULL; + + remap_block (&new, id); + gcc_assert (new != block); + for (t = BLOCK_SUBBLOCKS (block); t ; t = BLOCK_CHAIN (t)) + add_lexical_block (new, remap_blocks (t, id)); + return new; +} + static void copy_statement_list (tree *tp) { @@ -445,51 +508,46 @@ copy_bind_expr (tree *tp, int *walk_subtrees, inline_data *id) BIND_EXPR_VARS (*tp) = remap_decls (BIND_EXPR_VARS (*tp), id); } -/* Called from copy_body via walk_tree. DATA is really an `inline_data *'. */ +/* Called from copy_body_id via walk_tree. DATA is really an + `inline_data *'. */ static tree copy_body_r (tree *tp, int *walk_subtrees, void *data) { inline_data *id = (inline_data *) data; - tree fn = VARRAY_TOP_TREE (id->fns); + tree fn = id->callee; + tree new_block; -#if 0 - /* All automatic variables should have a DECL_CONTEXT indicating - what function they come from. */ - if ((TREE_CODE (*tp) == VAR_DECL || TREE_CODE (*tp) == LABEL_DECL) - && DECL_NAMESPACE_SCOPE_P (*tp)) - gcc_assert (DECL_EXTERNAL (*tp) || TREE_STATIC (*tp)); -#endif + /* Begin by recognizing trees that we'll completely rewrite for the + inlining context. Our output for these trees is completely + different from out input (e.g. RETURN_EXPR is deleted, and morphs + into an edge). Further down, we'll handle trees that get + duplicated and/or tweaked. */ - /* If this is a RETURN_EXPR, change it into a MODIFY_EXPR and a - GOTO_EXPR with the RET_LABEL as its target. */ - if (TREE_CODE (*tp) == RETURN_EXPR && id->ret_label) + /* If this is a RETURN_STMT, change it into an EXPR_STMT and a + GOTO_STMT with the RET_LABEL as its target. */ + if (TREE_CODE (*tp) == RETURN_EXPR && inlining_p (id)) { - tree return_stmt = *tp; - tree goto_stmt; - - /* Build the GOTO_EXPR. */ - tree assignment = TREE_OPERAND (return_stmt, 0); - goto_stmt = build1 (GOTO_EXPR, void_type_node, id->ret_label); - TREE_USED (id->ret_label) = 1; + tree assignment = TREE_OPERAND (*tp, 0); /* If we're returning something, just turn that into an - assignment into the equivalent of the original - RESULT_DECL. */ - if (assignment) - { - /* Do not create a statement containing a naked RESULT_DECL. */ - if (TREE_CODE (assignment) == RESULT_DECL) - gimplify_stmt (&assignment); - - *tp = build (BIND_EXPR, void_type_node, NULL, NULL, NULL); - 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; + assignment into the equivalent of the original RESULT_DECL. + If the "assignment" is just the result decl, the result + decl has already been set (e.g. a recent "foo (&result_decl, + ...)"); just toss the entire RETURN_EXPR. */ + if (assignment && TREE_CODE (assignment) == MODIFY_EXPR) + { + /* Replace the RETURN_EXPR with (a copy of) the + MODIFY_EXPR hanging underneath. */ + *tp = copy_node (assignment); + } + else /* Else the RETURN_EXPR returns no value. */ + { + *tp = NULL; + return (void *)1; + } } + /* Local variables and labels need to be replaced by equivalent variables. We don't want to copy static variables; there's only one of those, no matter how many times we inline the containing @@ -504,11 +562,17 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data) /* Replace this variable with the copy. */ STRIP_TYPE_NOPS (new_decl); *tp = new_decl; + *walk_subtrees = 0; } 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, walk_subtrees); + else if (TREE_CODE (*tp) == LABEL_DECL + && (! DECL_CONTEXT (*tp) + || decl_function_context (*tp) == id->callee)) + /* These may need to be remapped for EH handling. */ + *tp = remap_decl (*tp, id); else if (TREE_CODE (*tp) == BIND_EXPR) copy_bind_expr (tp, walk_subtrees, id); /* Types may need remapping as well. */ @@ -517,7 +581,7 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data) /* If this is a constant, we have to copy the node iff the type will be remapped. copy_tree_r will not copy a constant. */ - else if (TREE_CODE_CLASS (TREE_CODE (*tp)) == tcc_constant) + else if (CONSTANT_CLASS_P (*tp)) { tree new_type = remap_type (TREE_TYPE (*tp), id); @@ -538,8 +602,9 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data) knows not to copy VAR_DECLs, etc., so this is safe. */ else { - tree old_node = *tp; - + /* Here we handle trees that are not completely rewritten. + First we detect some inlining-induced bogosities for + discarding. */ if (TREE_CODE (*tp) == MODIFY_EXPR && TREE_OPERAND (*tp, 0) == TREE_OPERAND (*tp, 1) && (lang_hooks.tree_inlining.auto_var_in_fn_p @@ -563,54 +628,66 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data) } } } - else if (TREE_CODE (*tp) == INDIRECT_REF) + else if (TREE_CODE (*tp) == INDIRECT_REF + && !id->versioning_p) { /* Get rid of *& from inline substitutions that can happen when a pointer argument is an ADDR_EXPR. */ - tree decl = TREE_OPERAND (*tp, 0), value; + tree decl = TREE_OPERAND (*tp, 0); 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 - && (lang_hooks.types_compatible_p - (TREE_TYPE (*tp), TREE_TYPE (TREE_OPERAND (value, 0))))) - { - *tp = TREE_OPERAND (value, 0); - return copy_body_r (tp, walk_subtrees, data); + tree new; + /* If we happen to get an ADDR_EXPR in n->value, strip + it manually here as we'll eventually get ADDR_EXPRs + which lie about their types pointed to. In this case + build_fold_indirect_ref wouldn't strip the INDIRECT_REF, + but we absolutely rely on that. As fold_indirect_ref + does other useful transformations, try that first, though. */ + tree type = TREE_TYPE (TREE_TYPE ((tree)n->value)); + new = unshare_expr ((tree)n->value); + *tp = fold_indirect_ref_1 (type, new); + if (! *tp) + { + if (TREE_CODE (new) == ADDR_EXPR) + *tp = TREE_OPERAND (new, 0); + else + *tp = build1 (INDIRECT_REF, type, new); } + *walk_subtrees = 0; + return NULL; } } - copy_tree_r (tp, walk_subtrees, NULL); - - if (TREE_CODE (*tp) == CALL_EXPR && id->node && get_callee_fndecl (*tp)) + /* Here is the "usual case". Copy this tree node, and then + tweak some special cases. */ + copy_tree_r (tp, walk_subtrees, id->versioning_p ? data : NULL); + + /* If EXPR has block defined, map it to newly constructed block. + When inlining we want EXPRs without block appear in the block + of function call. */ + if (IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (TREE_CODE (*tp)))) { - if (id->saving_p) - { - struct cgraph_node *node; - struct cgraph_edge *edge; - - for (node = id->node->next_clone; node; node = node->next_clone) - { - edge = cgraph_edge (node, old_node); - gcc_assert (edge); - edge->call_expr = *tp; - } - } - else + new_block = id->block; + if (TREE_BLOCK (*tp)) { - struct cgraph_edge *edge - = cgraph_edge (id->current_node, old_node); - - if (edge) - cgraph_clone_edge (edge, id->node, *tp); + splay_tree_node n; + n = splay_tree_lookup (id->decl_map, + (splay_tree_key) TREE_BLOCK (*tp)); + gcc_assert (n); + new_block = (tree) n->value; } + TREE_BLOCK (*tp) = new_block; } + if (TREE_CODE (*tp) == RESX_EXPR && id->eh_region_offset) + TREE_OPERAND (*tp, 0) = + build_int_cst + (NULL_TREE, + id->eh_region_offset + TREE_INT_CST_LOW (TREE_OPERAND (*tp, 0))); + TREE_TYPE (*tp) = remap_type (TREE_TYPE (*tp), id); /* The copied TARGET_EXPR has never been expanded, even if the @@ -636,25 +713,338 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data) return NULL_TREE; } +/* Copy basic block, scale profile accordingly. Edges will be taken care of + later */ + +static basic_block +copy_bb (inline_data *id, basic_block bb, int frequency_scale, int count_scale) +{ + block_stmt_iterator bsi, copy_bsi; + basic_block copy_basic_block; + + /* create_basic_block() will append every new block to + basic_block_info automatically. */ + copy_basic_block = create_basic_block (NULL, (void *) 0, bb->prev_bb->aux); + copy_basic_block->count = bb->count * count_scale / REG_BR_PROB_BASE; + copy_basic_block->frequency = (bb->frequency + * frequency_scale / REG_BR_PROB_BASE); + copy_bsi = bsi_start (copy_basic_block); + + for (bsi = bsi_start (bb); + !bsi_end_p (bsi); bsi_next (&bsi)) + { + tree stmt = bsi_stmt (bsi); + tree orig_stmt = stmt; + + walk_tree (&stmt, copy_body_r, id, NULL); + + /* RETURN_EXPR might be removed, + this is signalled by making stmt pointer NULL. */ + if (stmt) + { + tree call, decl; + bsi_insert_after (©_bsi, stmt, BSI_NEW_STMT); + call = get_call_expr_in (stmt); + /* We're duplicating a CALL_EXPR. Find any corresponding + callgraph edges and update or duplicate them. */ + if (call && (decl = get_callee_fndecl (call))) + { + if (id->saving_p) + { + struct cgraph_node *node; + struct cgraph_edge *edge; + + /* We're saving a copy of the body, so we'll update the + callgraph nodes in place. Note that we avoid + altering the original callgraph node; we begin with + the first clone. */ + for (node = id->node->next_clone; + node; + node = node->next_clone) + { + edge = cgraph_edge (node, orig_stmt); + gcc_assert (edge); + edge->call_stmt = stmt; + } + } + else + { + struct cgraph_edge *edge; + + /* We're cloning or inlining this body; duplicate the + associate callgraph nodes. */ + if (!id->versioning_p) + { + edge = cgraph_edge (id->current_node, orig_stmt); + if (edge) + cgraph_clone_edge (edge, id->node, stmt, + REG_BR_PROB_BASE, 1, true); + } + } + if (id->versioning_p) + { + /* Update the call_expr on the edges from the new version + to its callees. */ + struct cgraph_edge *edge; + edge = cgraph_edge (id->node, orig_stmt); + if (edge) + edge->call_stmt = stmt; + } + } + /* If you think we can abort here, you are wrong. + There is no region 0 in tree land. */ + gcc_assert (lookup_stmt_eh_region_fn (id->callee_cfun, orig_stmt) + != 0); + + if (tree_could_throw_p (stmt)) + { + int region = lookup_stmt_eh_region_fn (id->callee_cfun, orig_stmt); + /* Add an entry for the copied tree in the EH hashtable. + When saving or cloning or versioning, use the hashtable in + cfun, and just copy the EH number. When inlining, use the + hashtable in the caller, and adjust the region number. */ + if (region > 0) + add_stmt_to_eh_region (stmt, region + id->eh_region_offset); + + /* If this tree doesn't have a region associated with it, + and there is a "current region," + then associate this tree with the current region + and add edges associated with this region. */ + if ((lookup_stmt_eh_region_fn (id->callee_cfun, + orig_stmt) <= 0 + && id->eh_region > 0) + && tree_could_throw_p (stmt)) + add_stmt_to_eh_region (stmt, id->eh_region); + } + } + } + return copy_basic_block; +} + +/* Copy edges from BB into its copy constructed earlier, scale profile + accordingly. Edges will be taken care of later. Assume aux + pointers to point to the copies of each BB. */ +static void +copy_edges_for_bb (basic_block bb, int count_scale) +{ + basic_block new_bb = bb->aux; + edge_iterator ei; + edge old_edge; + block_stmt_iterator bsi; + int flags; + + /* Use the indices from the original blocks to create edges for the + new ones. */ + FOR_EACH_EDGE (old_edge, ei, bb->succs) + if (!(old_edge->flags & EDGE_EH)) + { + edge new; + + flags = old_edge->flags; + + /* Return edges do get a FALLTHRU flag when the get inlined. */ + if (old_edge->dest->index == EXIT_BLOCK && !old_edge->flags + && old_edge->dest->aux != EXIT_BLOCK_PTR) + flags |= EDGE_FALLTHRU; + new = make_edge (new_bb, old_edge->dest->aux, flags); + new->count = old_edge->count * count_scale / REG_BR_PROB_BASE; + new->probability = old_edge->probability; + } + + if (bb->index == ENTRY_BLOCK || bb->index == EXIT_BLOCK) + return; + + for (bsi = bsi_start (new_bb); !bsi_end_p (bsi);) + { + tree copy_stmt; + + copy_stmt = bsi_stmt (bsi); + update_stmt (copy_stmt); + /* Do this before the possible split_block. */ + bsi_next (&bsi); + + /* If this tree could throw an exception, there are two + cases where we need to add abnormal edge(s): the + tree wasn't in a region and there is a "current + region" in the caller; or the original tree had + EH edges. In both cases split the block after the tree, + and add abnormal edge(s) as needed; we need both + those from the callee and the caller. + We check whether the copy can throw, because the const + propagation can change an INDIRECT_REF which throws + into a COMPONENT_REF which doesn't. If the copy + can throw, the original could also throw. */ + + if (tree_can_throw_internal (copy_stmt)) + { + if (!bsi_end_p (bsi)) + /* Note that bb's predecessor edges aren't necessarily + right at this point; split_block doesn't care. */ + { + edge e = split_block (new_bb, copy_stmt); + new_bb = e->dest; + bsi = bsi_start (new_bb); + } + + make_eh_edges (copy_stmt); + } + } +} + +/* Wrapper for remap_decl so it can be used as a callback. */ +static tree +remap_decl_1 (tree decl, void *data) +{ + return remap_decl (decl, data); +} + +/* Make a copy of the body of FN so that it can be inserted inline in + another function. Walks FN via CFG, returns new fndecl. */ + +static tree +copy_cfg_body (inline_data * id, gcov_type count, int frequency, + basic_block entry_block_map, basic_block exit_block_map) +{ + tree callee_fndecl = id->callee; + /* Original cfun for the callee, doesn't change. */ + struct function *callee_cfun = DECL_STRUCT_FUNCTION (callee_fndecl); + /* Copy, built by this function. */ + struct function *new_cfun; + /* Place to copy from; when a copy of the function was saved off earlier, + use that instead of the main copy. */ + struct function *cfun_to_copy = + (struct function *) ggc_alloc_cleared (sizeof (struct function)); + basic_block bb; + tree new_fndecl = NULL; + bool saving_or_cloning; + int count_scale, frequency_scale; + + if (ENTRY_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->count) + count_scale = (REG_BR_PROB_BASE * count + / ENTRY_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->count); + else + count_scale = 1; + + if (ENTRY_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->frequency) + frequency_scale = (REG_BR_PROB_BASE * frequency + / + ENTRY_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->frequency); + else + frequency_scale = count_scale; + + /* Register specific tree functions. */ + tree_register_cfg_hooks (); + + /* Must have a CFG here at this point. */ + gcc_assert (ENTRY_BLOCK_PTR_FOR_FUNCTION + (DECL_STRUCT_FUNCTION (callee_fndecl))); + + *cfun_to_copy = *DECL_STRUCT_FUNCTION (callee_fndecl); + + /* If there is a saved_cfg+saved_args lurking in the + struct function, a copy of the callee body was saved there, and + the 'struct cgraph edge' nodes have been fudged to point into the + saved body. Accordingly, we want to copy that saved body so the + callgraph edges will be recognized and cloned properly. */ + if (cfun_to_copy->saved_cfg) + { + cfun_to_copy->cfg = cfun_to_copy->saved_cfg; + cfun_to_copy->eh = cfun_to_copy->saved_eh; + } + id->callee_cfun = cfun_to_copy; + + /* If saving or cloning a function body, create new basic_block_info + and label_to_block_maps. Otherwise, we're duplicating a function + body for inlining; insert our new blocks and labels into the + existing varrays. */ + saving_or_cloning = (id->saving_p || id->cloning_p || id->versioning_p); + if (saving_or_cloning) + { + new_cfun = + (struct function *) ggc_alloc_cleared (sizeof (struct function)); + *new_cfun = *DECL_STRUCT_FUNCTION (callee_fndecl); + new_cfun->cfg = NULL; + new_cfun->decl = new_fndecl = copy_node (callee_fndecl); + new_cfun->ib_boundaries_block = (varray_type) 0; + DECL_STRUCT_FUNCTION (new_fndecl) = new_cfun; + push_cfun (new_cfun); + init_empty_tree_cfg (); + + ENTRY_BLOCK_PTR->count = + (ENTRY_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->count * count_scale / + REG_BR_PROB_BASE); + ENTRY_BLOCK_PTR->frequency = + (ENTRY_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->frequency * + frequency_scale / REG_BR_PROB_BASE); + EXIT_BLOCK_PTR->count = + (EXIT_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->count * count_scale / + REG_BR_PROB_BASE); + EXIT_BLOCK_PTR->frequency = + (EXIT_BLOCK_PTR_FOR_FUNCTION (callee_cfun)->frequency * + frequency_scale / REG_BR_PROB_BASE); + + entry_block_map = ENTRY_BLOCK_PTR; + exit_block_map = EXIT_BLOCK_PTR; + } + + ENTRY_BLOCK_PTR_FOR_FUNCTION (cfun_to_copy)->aux = entry_block_map; + EXIT_BLOCK_PTR_FOR_FUNCTION (cfun_to_copy)->aux = exit_block_map; + + + /* Duplicate any exception-handling regions. */ + if (cfun->eh) + { + if (saving_or_cloning) + init_eh_for_function (); + id->eh_region_offset = duplicate_eh_regions (cfun_to_copy, + remap_decl_1, + id, id->eh_region); + gcc_assert (inlining_p (id) || !id->eh_region_offset); + } + /* Use aux pointers to map the original blocks to copy. */ + FOR_EACH_BB_FN (bb, cfun_to_copy) + bb->aux = copy_bb (id, bb, frequency_scale, count_scale); + /* Now that we've duplicated the blocks, duplicate their edges. */ + FOR_ALL_BB_FN (bb, cfun_to_copy) + copy_edges_for_bb (bb, count_scale); + FOR_ALL_BB_FN (bb, cfun_to_copy) + bb->aux = NULL; + + if (saving_or_cloning) + pop_cfun (); + + return new_fndecl; +} + /* Make a copy of the body of FN so that it can be inserted inline in another function. */ static tree -copy_body (inline_data *id) +copy_generic_body (inline_data *id) { tree body; - tree fndecl = VARRAY_TOP_TREE (id->fns); + tree fndecl = id->callee; - if (fndecl == current_function_decl - && cfun->saved_tree) - body = cfun->saved_tree; - else - body = DECL_SAVED_TREE (fndecl); + body = DECL_SAVED_TREE (fndecl); walk_tree (&body, copy_body_r, id, NULL); return body; } +static tree +copy_body (inline_data *id, gcov_type count, int frequency, + basic_block entry_block_map, basic_block exit_block_map) +{ + tree fndecl = id->callee; + tree body; + + /* If this body has a CFG, walk CFG and copy. */ + gcc_assert (ENTRY_BLOCK_PTR_FOR_FUNCTION (DECL_STRUCT_FUNCTION (fndecl))); + body = copy_cfg_body (id, count, frequency, entry_block_map, exit_block_map); + + return body; +} + /* Return true if VALUE is an ADDR_EXPR of an automatic variable defined in function FN, or of a data member thereof. */ @@ -667,16 +1057,17 @@ self_inlining_addr_expr (tree value, tree fn) return false; var = get_base_address (TREE_OPERAND (value, 0)); - + return var && lang_hooks.tree_inlining.auto_var_in_fn_p (var, fn); } static void setup_one_parameter (inline_data *id, tree p, tree value, tree fn, - tree *init_stmts, tree *vars, bool *gimplify_init_stmts_p) + basic_block bb, tree *vars) { 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 @@ -685,11 +1076,6 @@ setup_one_parameter (inline_data *id, tree p, tree value, tree fn, && !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. */ /* 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 @@ -711,12 +1097,25 @@ setup_one_parameter (inline_data *id, tree p, tree value, tree fn, /* Make an equivalent VAR_DECL. Note that we must NOT remap the type here since the type of this decl must be visible to the calling function. */ - var = copy_decl_for_inlining (p, fn, VARRAY_TREE (id->fns, 0)); + var = copy_decl_for_dup (p, fn, id->caller, /*versioning=*/false); + + /* 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 = build_fold_indirect_ref (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); + insert_decl_map (id, p, var_sub); /* Declare this new variable. */ TREE_CHAIN (var) = *vars; @@ -742,6 +1141,7 @@ setup_one_parameter (inline_data *id, tree p, tree value, tree fn, if (value) { tree rhs = fold_convert (TREE_TYPE (var), value); + block_stmt_iterator bsi = bsi_last (bb); if (rhs == error_mark_node) return; @@ -749,33 +1149,35 @@ setup_one_parameter (inline_data *id, tree p, tree value, tree fn, /* 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 + tree code, not its operand. Thus the explicit check that its 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; + gimplify_stmt (&init_stmt); + + /* If VAR represents a zero-sized variable, it's possible that the + assignment statment may result in no gimple statements. */ + if (init_stmt) + bsi_insert_after (&bsi, init_stmt, BSI_NEW_STMT); } } /* 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 +static void initialize_inlined_parameters (inline_data *id, tree args, tree static_chain, - tree fn, tree bind_expr) + tree fn, basic_block bb) { - tree init_stmts = NULL_TREE; tree parms; tree a; tree p; tree vars = NULL_TREE; - bool gimplify_init_stmts_p = false; int argnum = 0; /* Figure out what the parameters are. */ @@ -796,37 +1198,30 @@ initialize_inlined_parameters (inline_data *id, tree args, tree static_chain, value = lang_hooks.tree_inlining.convert_parm_for_inlining (p, a ? TREE_VALUE (a) : NULL_TREE, fn, argnum); - setup_one_parameter (id, p, value, fn, &init_stmts, &vars, - &gimplify_init_stmts_p); - } - - /* Evaluate trailing arguments. */ - for (; a; a = TREE_CHAIN (a)) - { - tree value = TREE_VALUE (a); - append_to_statement_list (value, &init_stmts); + setup_one_parameter (id, p, value, fn, bb, &vars); } /* Initialize the static chain. */ p = DECL_STRUCT_FUNCTION (fn)->static_chain_decl; + if (fn == current_function_decl) + p = DECL_STRUCT_FUNCTION (fn)->saved_static_chain_decl; if (p) { /* No static chain? Seems like a bug in tree-nested.c. */ gcc_assert (static_chain); - setup_one_parameter (id, p, static_chain, fn, &init_stmts, &vars, - &gimplify_init_stmts_p); + setup_one_parameter (id, p, static_chain, fn, bb, &vars); } - if (gimplify_init_stmts_p) - gimplify_body (&init_stmts, current_function_decl); - - declare_inline_vars (bind_expr, vars); - return init_stmts; + declare_inline_vars (id->block, vars); } -/* Declare a return variable to replace the RESULT_DECL for the function we - are calling. RETURN_SLOT_ADDR, if non-null, was a fake parameter that +/* Declare a return variable to replace the RESULT_DECL for the + function we are calling. An appropriate DECL_STMT is returned. + The USE_STMT is filled to contain a use of the declaration to + indicate the return value of the function. + + RETURN_SLOT_ADDR, if non-null, was a fake parameter that took the address of the result. MODIFY_DEST, if non-null, was the LHS of the MODIFY_EXPR to which this call is the RHS. @@ -838,8 +1233,8 @@ static tree declare_return_variable (inline_data *id, tree return_slot_addr, tree modify_dest, tree *use_p) { - tree callee = VARRAY_TOP_TREE (id->fns); - tree caller = VARRAY_TREE (id->fns, 0); + tree callee = id->callee; + tree caller = id->caller; tree result = DECL_RESULT (callee); tree callee_type = TREE_TYPE (result); tree caller_type = TREE_TYPE (TREE_TYPE (callee)); @@ -889,10 +1284,21 @@ declare_return_variable (inline_data *id, tree return_slot_addr, /* If the callee cannot possibly modify MODIFY_DEST, then we can reuse it as the result of the call directly. Don't do this if it would promote MODIFY_DEST to addressable. */ - else if (!TREE_STATIC (modify_dest) - && !TREE_ADDRESSABLE (modify_dest) - && !TREE_ADDRESSABLE (result)) - use_it = true; + else if (TREE_ADDRESSABLE (result)) + use_it = false; + else + { + tree base_m = get_base_address (modify_dest); + + /* If the base isn't a decl, then it's a pointer, and we don't + know where that's going to go. */ + if (!DECL_P (base_m)) + use_it = false; + else if (is_global_var (base_m)) + use_it = false; + else if (!TREE_ADDRESSABLE (base_m)) + use_it = true; + } if (use_it) { @@ -904,7 +1310,8 @@ declare_return_variable (inline_data *id, tree return_slot_addr, gcc_assert (TREE_CODE (TYPE_SIZE_UNIT (callee_type)) == INTEGER_CST); - var = copy_decl_for_inlining (result, callee, caller); + var = copy_decl_for_dup (result, callee, caller, /*versioning=*/false); + DECL_SEEN_IN_BIND_EXPR_P (var) = 1; DECL_STRUCT_FUNCTION (caller)->unexpanded_var_list = tree_cons (NULL_TREE, var, @@ -963,7 +1370,7 @@ inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED, && !lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn))) { inline_forbidden_reason - = N_("%Jfunction %qF can never be inlined because it uses " + = G_("function %q+F can never be inlined because it uses " "alloca (override using the always_inline attribute)"); return node; } @@ -975,7 +1382,7 @@ inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED, if (setjmp_call_p (t)) { inline_forbidden_reason - = N_("%Jfunction %qF can never be inlined because it uses setjmp"); + = G_("function %q+F can never be inlined because it uses setjmp"); return node; } @@ -989,7 +1396,7 @@ inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED, case BUILT_IN_NEXT_ARG: case BUILT_IN_VA_END: inline_forbidden_reason - = N_("%Jfunction %qF can never be inlined because it " + = G_("function %q+F can never be inlined because it " "uses variable argument lists"); return node; @@ -1000,17 +1407,28 @@ inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED, function calling __builtin_longjmp to be inlined into the function calling __builtin_setjmp, Things will Go Awry. */ inline_forbidden_reason - = N_("%Jfunction %qF can never be inlined because " + = G_("function %q+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 %qF can never be inlined because " + = G_("function %q+F can never be inlined because " "it uses non-local goto"); return node; + case BUILT_IN_RETURN: + case BUILT_IN_APPLY_ARGS: + /* If a __builtin_apply_args caller would be inlined, + it would be saving arguments of the function it has + been inlined into. Similarly __builtin_return would + return from the function the inline has been inlined into. */ + inline_forbidden_reason + = G_("function %q+F can never be inlined because " + "it uses __builtin_return or __builtin_apply_args"); + return node; + default: break; } @@ -1026,7 +1444,7 @@ inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED, if (TREE_CODE (t) != LABEL_DECL) { inline_forbidden_reason - = N_("%Jfunction %qF can never be inlined " + = G_("function %q+F can never be inlined " "because it contains a computed goto"); return node; } @@ -1040,7 +1458,7 @@ inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED, because we cannot remap the destination label used in the function that is performing the non-local goto. */ inline_forbidden_reason - = N_("%Jfunction %qF can never be inlined " + = G_("function %q+F can never be inlined " "because it receives a non-local goto"); return node; } @@ -1057,7 +1475,7 @@ inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED, UNION_TYPE nodes, then it goes into infinite recursion on a structure containing a pointer to its own type. If it doesn't, then the type node for S doesn't get adjusted properly when - F is inlined, and we abort in find_function_data. + F is inlined. ??? This is likely no longer true, but it's too late in the 4.0 cycle to try to find out. This should be checked for 4.1. */ @@ -1065,7 +1483,7 @@ inline_forbidden_p_1 (tree *nodep, int *walk_subtrees ATTRIBUTE_UNUSED, if (variably_modified_type_p (TREE_TYPE (t), NULL)) { inline_forbidden_reason - = N_("%Jfunction %qF can never be inlined " + = G_("function %q+F can never be inlined " "because it uses variable sized variables"); return node; } @@ -1082,9 +1500,20 @@ static tree inline_forbidden_p (tree fndecl) { location_t saved_loc = input_location; - tree ret = walk_tree_without_duplicates (&DECL_SAVED_TREE (fndecl), - inline_forbidden_p_1, fndecl); + block_stmt_iterator bsi; + basic_block bb; + tree ret = NULL_TREE; + + FOR_EACH_BB_FN (bb, DECL_STRUCT_FUNCTION (fndecl)) + for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi)) + { + ret = walk_tree_without_duplicates (bsi_stmt_ptr (bsi), + inline_forbidden_p_1, fndecl); + if (ret) + goto egress; + } +egress: input_location = saved_loc; return ret; } @@ -1149,9 +1578,9 @@ inlinable_function_p (tree fn) && !DECL_IN_SYSTEM_HEADER (fn)); if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn))) - sorry (inline_forbidden_reason, fn, fn); + sorry (inline_forbidden_reason, fn); else if (do_warning) - warning (inline_forbidden_reason, fn, fn); + warning (OPT_Winline, inline_forbidden_reason, fn); inlinable = false; } @@ -1162,6 +1591,23 @@ inlinable_function_p (tree fn) return inlinable; } +/* Estimate the cost of a memory move. Use machine dependent + word size and take possible memcpy call into account. */ + +int +estimate_move_cost (tree type) +{ + HOST_WIDE_INT size; + + size = int_size_in_bytes (type); + + if (size < 0 || size > MOVE_MAX_PIECES * MOVE_RATIO) + /* Cost of a memcpy call, 3 arguments and the call. */ + return 4; + else + return ((size + MOVE_MAX_PIECES - 1) / MOVE_MAX_PIECES); +} + /* Used by estimate_num_insns. Estimate number of instructions seen by given statement. */ @@ -1191,6 +1637,8 @@ estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data) case COMPONENT_REF: case BIT_FIELD_REF: case INDIRECT_REF: + case ALIGN_INDIRECT_REF: + case MISALIGNED_INDIRECT_REF: case ARRAY_REF: case ARRAY_RANGE_REF: case OBJ_TYPE_REF: @@ -1227,7 +1675,7 @@ estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data) /* 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 + we are able to optimize the tree before estimating its size and break out static initializers. */ case IDENTIFIER_NODE: case INTEGER_CST: @@ -1238,29 +1686,52 @@ estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data) *walk_subtrees = 0; return NULL; - /* Recognize assignments of large structures and constructors of - big arrays. */ + /* Try to estimate the cost of assignments. We have three cases to + deal with: + 1) Simple assignments to registers; + 2) Stores to things that must live in memory. This includes + "normal" stores to scalars, but also assignments of large + structures, or constructors of big arrays; + 3) TARGET_EXPRs. + + Let us look at the first two cases, assuming we have "a = b + C": + > + If "a" is a GIMPLE register, the assignment to it is free on almost + any target, because "a" usually ends up in a real register. Hence + the only cost of this expression comes from the PLUS_EXPR, and we + can ignore the MODIFY_EXPR. + If "a" is not a GIMPLE register, the assignment to "a" will most + likely be a real store, so the cost of the MODIFY_EXPR is the cost + of moving something into "a", which we compute using the function + estimate_move_cost. + + The third case deals with TARGET_EXPRs, for which the semantics are + that a temporary is assigned, unless the TARGET_EXPR itself is being + assigned to something else. In the latter case we do not need the + temporary. E.g. in >, the + MODIFY_EXPR is free. */ case INIT_EXPR: case MODIFY_EXPR: - x = TREE_OPERAND (x, 0); - /* FALLTHRU */ - case TARGET_EXPR: - case CONSTRUCTOR: - { - HOST_WIDE_INT size; + /* Is the right and side a TARGET_EXPR? */ + if (TREE_CODE (TREE_OPERAND (x, 1)) == TARGET_EXPR) + break; + /* ... fall through ... */ - size = int_size_in_bytes (TREE_TYPE (x)); + case TARGET_EXPR: + x = TREE_OPERAND (x, 0); + /* Is this an assignments to a register? */ + if (is_gimple_reg (x)) + break; + /* Otherwise it's a store, so fall through to compute the move cost. */ - if (size < 0 || size > MOVE_MAX_PIECES * MOVE_RATIO) - *count += 10; - else - *count += ((size + MOVE_MAX_PIECES - 1) / MOVE_MAX_PIECES); - } + case CONSTRUCTOR: + *count += estimate_move_cost (TREE_TYPE (x)); break; - /* Assign cost of 1 to usual operations. - ??? We may consider mapping RTL costs to this. */ + /* Assign cost of 1 to usual operations. + ??? We may consider mapping RTL costs to this. */ case COND_EXPR: + case VEC_COND_EXPR: case PLUS_EXPR: case MINUS_EXPR: @@ -1281,6 +1752,8 @@ estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data) case RSHIFT_EXPR: case LROTATE_EXPR: case RROTATE_EXPR: + case VEC_LSHIFT_EXPR: + case VEC_RSHIFT_EXPR: case BIT_IOR_EXPR: case BIT_XOR_EXPR: @@ -1323,6 +1796,12 @@ estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data) case ASM_EXPR: + case REALIGN_LOAD_EXPR: + + case REDUC_MAX_EXPR: + case REDUC_MIN_EXPR: + case REDUC_PLUS_EXPR: + case RESX_EXPR: *count += 1; break; @@ -1344,8 +1823,9 @@ estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data) case CALL_EXPR: { tree decl = get_callee_fndecl (x); + tree arg; - if (decl && DECL_BUILT_IN (decl)) + if (decl && DECL_BUILT_IN_CLASS (decl) == BUILT_IN_NORMAL) switch (DECL_FUNCTION_CODE (decl)) { case BUILT_IN_CONSTANT_P: @@ -1356,11 +1836,24 @@ estimate_num_insns_1 (tree *tp, int *walk_subtrees, void *data) default: break; } - *count += 10; + + /* Our cost must be kept in sync with cgraph_estimate_size_after_inlining + that does use function declaration to figure out the arguments. */ + if (!decl) + { + for (arg = TREE_OPERAND (x, 1); arg; arg = TREE_CHAIN (arg)) + *count += estimate_move_cost (TREE_TYPE (TREE_VALUE (arg))); + } + else + { + for (arg = DECL_ARGUMENTS (decl); arg; arg = TREE_CHAIN (arg)) + *count += estimate_move_cost (TREE_TYPE (arg)); + } + + *count += PARAM_VALUE (PARAM_INLINE_CALL_COST); break; } default: - /* Abort here se we know we don't miss any nodes. */ gcc_unreachable (); } return NULL; @@ -1372,31 +1865,95 @@ int estimate_num_insns (tree expr) { int num = 0; - walk_tree_without_duplicates (&expr, estimate_num_insns_1, &num); + struct pointer_set_t *visited_nodes; + basic_block bb; + block_stmt_iterator bsi; + struct function *my_function; + + /* If we're given an entire function, walk the CFG. */ + if (TREE_CODE (expr) == FUNCTION_DECL) + { + my_function = DECL_STRUCT_FUNCTION (expr); + gcc_assert (my_function && my_function->cfg); + visited_nodes = pointer_set_create (); + FOR_EACH_BB_FN (bb, my_function) + { + for (bsi = bsi_start (bb); + !bsi_end_p (bsi); + bsi_next (&bsi)) + { + walk_tree (bsi_stmt_ptr (bsi), estimate_num_insns_1, + &num, visited_nodes); + } + } + pointer_set_destroy (visited_nodes); + } + else + walk_tree_without_duplicates (&expr, estimate_num_insns_1, &num); + return num; } +typedef struct function *function_p; + +DEF_VEC_P(function_p); +DEF_VEC_ALLOC_P(function_p,heap); + +/* Initialized with NOGC, making this poisonous to the garbage collector. */ +static VEC(function_p,heap) *cfun_stack; + +void +push_cfun (struct function *new_cfun) +{ + VEC_safe_push (function_p, heap, cfun_stack, cfun); + cfun = new_cfun; +} + +void +pop_cfun (void) +{ + cfun = VEC_pop (function_p, cfun_stack); +} + +/* Install new lexical TREE_BLOCK underneath 'current_block'. */ +static void +add_lexical_block (tree current_block, tree new_block) +{ + tree *blk_p; + + /* Walk to the last sub-block. */ + for (blk_p = &BLOCK_SUBBLOCKS (current_block); + *blk_p; + blk_p = &TREE_CHAIN (*blk_p)) + ; + *blk_p = new_block; + BLOCK_SUPERCONTEXT (new_block) = current_block; +} + /* If *TP is a CALL_EXPR, replace it with its inline expansion. */ -static tree -expand_call_inline (tree *tp, int *walk_subtrees, void *data) +static bool +expand_call_inline (basic_block bb, tree stmt, tree *tp, void *data) { inline_data *id; tree t; - tree expr; - tree stmt; tree use_retvar; - tree decl; tree fn; - tree arg_inits; - tree *inlined_body; splay_tree st; tree args; tree return_slot_addr; tree modify_dest; location_t saved_location; - struct cgraph_edge *edge; + struct cgraph_edge *cg_edge; const char *reason; + basic_block return_block; + edge e; + block_stmt_iterator bsi, stmt_bsi; + bool successfully_inlined = FALSE; + tree t_step; + tree var; + struct cgraph_node *old_node; + tree decl; /* See what we've got. */ id = (inline_data *) data; @@ -1408,39 +1965,6 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data) 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) - { -#if 0 - int i, len = TREE_CODE_LENGTH (TARGET_EXPR); - - /* We're walking our own subtrees. */ - *walk_subtrees = 0; - - /* Actually walk over them. This loop is the body of - walk_trees, omitting the case where the TARGET_EXPR - itself is handled. */ - for (i = 0; i < len; ++i) - { - if (i == 2) - ++id->in_target_cleanup_p; - walk_tree (&TREE_OPERAND (*tp, i), expand_call_inline, data, - id->tree_pruner); - if (i == 2) - --id->in_target_cleanup_p; - } - - goto egress; -#endif - } - - if (TYPE_P (t)) - /* Because types were not copied in copy_body, CALL_EXPRs beneath - them should not be expanded. This can happen if the type is a - dynamic array type, for example. */ - *walk_subtrees = 0; - /* From here on, we're only interested in CALL_EXPRs. */ if (TREE_CODE (t) != CALL_EXPR) goto egress; @@ -1471,11 +1995,11 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data) if (!id->current_node->analyzed) goto egress; - edge = cgraph_edge (id->current_node, t); + cg_edge = cgraph_edge (id->current_node, stmt); /* Constant propagation on argument done during previous inlining may create new direct call. Produce an edge for it. */ - if (!edge) + if (!cg_edge) { struct cgraph_node *dest = cgraph_node (fn); @@ -1484,47 +2008,77 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data) constant propagating arguments. In all other cases we hit a bug (incorrect node sharing is most common reason for missing edges. */ gcc_assert (dest->needed || !flag_unit_at_a_time); - cgraph_create_edge (id->node, dest, t)->inline_failed + cgraph_create_edge (id->node, dest, stmt, + bb->count, bb->loop_depth)->inline_failed = N_("originally indirect function call not considered for inlining"); goto egress; } /* Don't try to inline functions that are not well-suited to inlining. */ - if (!cgraph_inline_p (edge, &reason)) + if (!cgraph_inline_p (cg_edge, &reason)) { - if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn))) + if (lookup_attribute ("always_inline", DECL_ATTRIBUTES (fn)) + /* Avoid warnings during early inline pass. */ + && (!flag_unit_at_a_time || cgraph_global_info_ready)) { - sorry ("%Jinlining failed in call to %qF: %s", fn, fn, reason); + sorry ("inlining failed in call to %q+F: %s", fn, reason); sorry ("called from here"); } else if (warn_inline && DECL_DECLARED_INLINE_P (fn) && !DECL_IN_SYSTEM_HEADER (fn) && strlen (reason) - && !lookup_attribute ("noinline", DECL_ATTRIBUTES (fn))) + && !lookup_attribute ("noinline", DECL_ATTRIBUTES (fn)) + /* Avoid warnings during early inline pass. */ + && (!flag_unit_at_a_time || cgraph_global_info_ready)) { - warning ("%Jinlining failed in call to %qF: %s", fn, fn, reason); - warning ("called from here"); + warning (OPT_Winline, "inlining failed in call to %q+F: %s", + fn, reason); + warning (OPT_Winline, "called from here"); } goto egress; } #ifdef ENABLE_CHECKING - if (edge->callee->decl != id->node->decl) - verify_cgraph_node (edge->callee); + if (cg_edge->callee->decl != id->node->decl) + verify_cgraph_node (cg_edge->callee); #endif - if (! lang_hooks.tree_inlining.start_inlining (fn)) - goto egress; + /* We will be inlining this callee. */ + + id->eh_region = lookup_stmt_eh_region (stmt); + + /* Split the block holding the CALL_EXPR. */ + + e = split_block (bb, stmt); + bb = e->src; + return_block = e->dest; + remove_edge (e); + + /* split_block splits before the statement, work around this by moving + the call into the first half_bb. Not pretty, but seems easier than + doing the CFG manipulation by hand when the CALL_EXPR is in the last + statement in BB. */ + stmt_bsi = bsi_last (bb); + bsi = bsi_start (return_block); + if (!bsi_end_p (bsi)) + bsi_move_before (&stmt_bsi, &bsi); + else + { + tree stmt = bsi_stmt (stmt_bsi); + bsi_remove (&stmt_bsi); + bsi_insert_after (&bsi, stmt, BSI_NEW_STMT); + } + stmt_bsi = bsi_start (return_block); /* 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 (BIND_EXPR, void_type_node, NULL_TREE, - stmt, make_node (BLOCK)); - BLOCK_ABSTRACT_ORIGIN (BIND_EXPR_BLOCK (expr)) = fn; + id->block = make_node (BLOCK); + BLOCK_ABSTRACT_ORIGIN (id->block) = fn; + BLOCK_SOURCE_LOCATION (id->block) = input_location; + add_lexical_block (TREE_BLOCK (stmt), id->block); /* Local declarations will be replaced by their equivalents in this map. */ @@ -1534,228 +2088,156 @@ expand_call_inline (tree *tp, int *walk_subtrees, void *data) /* Initialize the parameters. */ args = TREE_OPERAND (t, 1); - return_slot_addr = NULL_TREE; - if (CALL_EXPR_HAS_RETURN_SLOT_ADDR (t)) - { - return_slot_addr = TREE_VALUE (args); - args = TREE_CHAIN (args); - TREE_TYPE (expr) = void_type_node; - } - - 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. - 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; + initialize_inlined_parameters (id, args, TREE_OPERAND (t, 2), fn, bb); - save_tsi = id->tsi; - expand_calls_inline (&arg_inits, id); - id->tsi = save_tsi; + /* Record the function we are about to inline. */ + id->callee = fn; - /* And add them to the tree. */ - append_to_statement_list (arg_inits, &BIND_EXPR_BODY (expr)); - } - - /* Record the function we are about to inline so that we can avoid - recursing into it. */ - VARRAY_PUSH_TREE (id->fns, fn); - - /* Record the function we are about to inline if optimize_function - has not been called on it yet and we don't have it in the list. */ - if (! DECL_INLINED_FNS (fn)) - { - int i; - - for (i = VARRAY_ACTIVE_SIZE (id->inlined_fns) - 1; i >= 0; i--) - if (VARRAY_TREE (id->inlined_fns, i) == fn) - break; - if (i < 0) - VARRAY_PUSH_TREE (id->inlined_fns, fn); - } + if (DECL_STRUCT_FUNCTION (fn)->saved_blocks) + add_lexical_block (id->block, remap_blocks (DECL_STRUCT_FUNCTION (fn)->saved_blocks, id)); + else if (DECL_INITIAL (fn)) + add_lexical_block (id->block, remap_blocks (DECL_INITIAL (fn), id)); /* 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); gcc_assert (DECL_INITIAL (fn)); gcc_assert (TREE_CODE (DECL_INITIAL (fn)) == BLOCK); /* Find the lhs to which the result of this call is assigned. */ - modify_dest = tsi_stmt (id->tsi); - if (TREE_CODE (modify_dest) == MODIFY_EXPR) - modify_dest = TREE_OPERAND (modify_dest, 0); + return_slot_addr = NULL; + if (TREE_CODE (stmt) == MODIFY_EXPR) + { + modify_dest = TREE_OPERAND (stmt, 0); + + /* The function which we are inlining might not return a value, + in which case we should issue a warning that the function + does not return a value. In that case the optimizers will + see that the variable to which the value is assigned was not + initialized. We do not want to issue a warning about that + uninitialized variable. */ + if (DECL_P (modify_dest)) + TREE_NO_WARNING (modify_dest) = 1; + if (CALL_EXPR_RETURN_SLOT_OPT (t)) + { + return_slot_addr = build_fold_addr_expr (modify_dest); + modify_dest = NULL; + } + } else modify_dest = NULL; /* Declare the return variable for the function. */ decl = declare_return_variable (id, return_slot_addr, - modify_dest, &use_retvar); + modify_dest, &use_retvar); + /* Do this only if declare_return_variable created a new one. */ + if (decl && !return_slot_addr && decl != modify_dest) + declare_inline_vars (id->block, decl); /* After we've initialized the parameters, we insert the body of the function itself. */ - { - struct cgraph_node *old_node = id->current_node; - - id->current_node = edge->callee; - 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. */ - if (TREE_USED (id->ret_label)) + old_node = id->current_node; + + /* Anoint the callee-to-be-duplicated as the "current_node." When + CALL_EXPRs within callee are duplicated, the edges from callee to + callee's callees (caller's grandchildren) will be cloned. */ + id->current_node = cg_edge->callee; + + /* This is it. Duplicate the callee body. Assume callee is + pre-gimplified. Note that we must not alter the caller + function in any way before this point, as this CALL_EXPR may be + a self-referential call; if we're calling ourselves, we need to + duplicate our body before altering anything. */ + copy_body (id, bb->count, bb->frequency, bb, return_block); + id->current_node = old_node; + + /* Add local vars in this inlined callee to caller. */ + t_step = id->callee_cfun->unexpanded_var_list; + if (id->callee_cfun->saved_unexpanded_var_list) + t_step = id->callee_cfun->saved_unexpanded_var_list; + for (; t_step; t_step = TREE_CHAIN (t_step)) { - tree label = build1 (LABEL_EXPR, void_type_node, id->ret_label); - append_to_statement_list (label, &BIND_EXPR_BODY (expr)); + var = TREE_VALUE (t_step); + if (TREE_STATIC (var) && !TREE_ASM_WRITTEN (var)) + cfun->unexpanded_var_list = tree_cons (NULL_TREE, var, + cfun->unexpanded_var_list); + else + cfun->unexpanded_var_list = tree_cons (NULL_TREE, remap_decl (var, id), + cfun->unexpanded_var_list); } /* Clean up. */ splay_tree_delete (id->decl_map); id->decl_map = st; - /* The new expression has side-effects if the old one did. */ - TREE_SIDE_EFFECTS (expr) = TREE_SIDE_EFFECTS (t); - - tsi_link_before (&id->tsi, expr, TSI_SAME_STMT); - /* 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 (!use_retvar || !modify_dest) - *tsi_stmt_ptr (id->tsi) = build_empty_stmt (); + clobber the CALL_EXPR with a reference to the return variable. */ + if (use_retvar && (TREE_CODE (bsi_stmt (stmt_bsi)) != CALL_EXPR)) + { + *tp = use_retvar; + maybe_clean_or_replace_eh_stmt (stmt, stmt); + } else - *tp = use_retvar; + /* We're modifying a TSI owned by gimple_expand_calls_inline(); + tsi_delink() will leave the iterator in a sane state. */ + bsi_remove (&stmt_bsi); - /* 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. + bsi_next (&bsi); + if (bsi_end_p (bsi)) + tree_purge_dead_eh_edges (return_block); - Unfortunately, that is wrong as inlining the function can create/expose - interesting side effects (such as setting of a return value). + /* 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 + the equivalent inlined version either. */ + TREE_USED (*tp) = 1; - The easiest solution is to simply recalculate TREE_SIDE_EFFECTS for - the toplevel expression. */ - recalculate_side_effects (expr); + /* Output the inlining info for this abstract function, since it has been + inlined. If we don't do this now, we can lose the information about the + variables in the function when the blocks get blown away as soon as we + remove the cgraph node. */ + (*debug_hooks->outlining_inline_function) (cg_edge->callee->decl); /* Update callgraph if needed. */ - cgraph_remove_node (edge->callee); + cgraph_remove_node (cg_edge->callee); - /* Recurse into the body of the just inlined function. */ - expand_calls_inline (inlined_body, id); - VARRAY_POP (id->fns); + /* Declare the 'auto' variables added with this inlined body. */ + record_vars (BLOCK_VARS (id->block)); + id->block = NULL_TREE; + successfully_inlined = TRUE; - /* Don't walk into subtrees. We've already handled them above. */ - *walk_subtrees = 0; - - lang_hooks.tree_inlining.end_inlining (fn); - - /* Keep iterating. */ egress: input_location = saved_location; - return NULL_TREE; + return successfully_inlined; } -static void -expand_calls_inline (tree *stmt_p, inline_data *id) +/* Expand call statements reachable from STMT_P. + We can only have CALL_EXPRs as the "toplevel" tree code or nested + in a MODIFY_EXPR. See tree-gimple.c:get_call_expr_in(). We can + unfortunately not use that function here because we need a pointer + to the CALL_EXPR, not the tree itself. */ + +static bool +gimple_expand_calls_inline (basic_block bb, inline_data *id) { - tree stmt = *stmt_p; - enum tree_code code = TREE_CODE (stmt); - int dummy; + block_stmt_iterator bsi; - switch (code) + /* Register specific tree functions. */ + tree_register_cfg_hooks (); + for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi)) { - case STATEMENT_LIST: - { - tree_stmt_iterator i; - tree new; - - for (i = tsi_start (stmt); !tsi_end_p (i); ) - { - id->tsi = i; - 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: - expand_calls_inline (&COND_EXPR_THEN (stmt), id); - expand_calls_inline (&COND_EXPR_ELSE (stmt), id); - break; - - case CATCH_EXPR: - expand_calls_inline (&CATCH_BODY (stmt), id); - break; - - case EH_FILTER_EXPR: - expand_calls_inline (&EH_FILTER_FAILURE (stmt), id); - break; - - case TRY_CATCH_EXPR: - case TRY_FINALLY_EXPR: - expand_calls_inline (&TREE_OPERAND (stmt, 0), id); - expand_calls_inline (&TREE_OPERAND (stmt, 1), id); - break; - - case BIND_EXPR: - expand_calls_inline (&BIND_EXPR_BODY (stmt), id); - break; - - case COMPOUND_EXPR: - /* We're gimple. We should have gotten rid of all these. */ - gcc_unreachable (); - - 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) == WITH_SIZE_EXPR) - { - stmt_p = &TREE_OPERAND (stmt, 0); - stmt = *stmt_p; - } - if (TREE_CODE (stmt) != CALL_EXPR) - break; - - /* FALLTHRU */ - - case CALL_EXPR: - expand_call_inline (stmt_p, &dummy, id); - break; - - default: - break; + tree *expr_p = bsi_stmt_ptr (bsi); + tree stmt = *expr_p; + + if (TREE_CODE (*expr_p) == MODIFY_EXPR) + expr_p = &TREE_OPERAND (*expr_p, 1); + if (TREE_CODE (*expr_p) == WITH_SIZE_EXPR) + expr_p = &TREE_OPERAND (*expr_p, 0); + if (TREE_CODE (*expr_p) == CALL_EXPR) + if (expand_call_inline (bb, stmt, expr_p, id)) + return true; } + return false; } /* Expand calls to inline functions in the body of FN. */ @@ -1765,8 +2247,7 @@ optimize_inline_calls (tree fn) { inline_data id; tree prev_fn; - tree ifn; - + basic_block bb; /* There is no point in performing inlining if errors have already occurred -- and we might crash if we try to inline invalid code. */ @@ -1777,38 +2258,31 @@ optimize_inline_calls (tree fn) memset (&id, 0, sizeof (id)); id.current_node = id.node = cgraph_node (fn); - /* Don't allow recursion into FN. */ - VARRAY_TREE_INIT (id.fns, 32, "fns"); - VARRAY_PUSH_TREE (id.fns, fn); + id.caller = fn; /* Or any functions that aren't finished yet. */ prev_fn = NULL_TREE; if (current_function_decl) { - VARRAY_PUSH_TREE (id.fns, current_function_decl); + id.caller = current_function_decl; prev_fn = current_function_decl; } + push_gimplify_context (); - prev_fn = lang_hooks.tree_inlining.add_pending_fn_decls (&id.fns, prev_fn); - - /* Create the list of functions this call will inline. */ - VARRAY_TREE_INIT (id.inlined_fns, 32, "inlined_fns"); + /* Reach the trees by walking over the CFG, and note the + enclosing basic-blocks in the call edges. */ + /* We walk the blocks going forward, because inlined function bodies + will split id->current_basic_block, and the new blocks will + follow it; we'll trudge through them, processing their CALL_EXPRs + along the way. */ + FOR_EACH_BB (bb) + gimple_expand_calls_inline (bb, &id); - /* Keep track of the low-water mark, i.e., the point where the first - real inlining is represented in ID.FNS. */ - id.first_inlined_fn = VARRAY_ACTIVE_SIZE (id.fns); - /* Replace all calls to inline functions with the bodies of those - functions. */ - id.tree_pruner = htab_create (37, htab_hash_pointer, htab_eq_pointer, NULL); - expand_calls_inline (&DECL_SAVED_TREE (fn), &id); - - /* Clean up. */ - htab_delete (id.tree_pruner); - ifn = make_tree_vec (VARRAY_ACTIVE_SIZE (id.inlined_fns)); - if (VARRAY_ACTIVE_SIZE (id.inlined_fns)) - memcpy (&TREE_VEC_ELT (ifn, 0), &VARRAY_TREE (id.inlined_fns, 0), - VARRAY_ACTIVE_SIZE (id.inlined_fns) * sizeof (tree)); - DECL_INLINED_FNS (fn) = ifn; + pop_gimplify_context (NULL); + /* Renumber the (code) basic_blocks consecutively. */ + compact_blocks (); + /* Renumber the lexical scoping (non-code) blocks consecutively. */ + number_blocks (fn); #ifdef ENABLE_CHECKING { @@ -1821,6 +2295,11 @@ optimize_inline_calls (tree fn) gcc_assert (e->inline_failed); } #endif + /* We need to rescale frequencies again to peak at REG_BR_PROB_BASE + as inlining loops might increase the maximum. */ + if (ENTRY_BLOCK_PTR->count) + counts_to_freqs (); + fold_cond_expr_cond (); } /* FN is a function that has a complete body, and CLONE is a function whose @@ -1833,34 +2312,42 @@ clone_body (tree clone, tree fn, void *arg_map) inline_data id; /* Clone the body, as if we were making an inline call. But, remap the - parameters in the callee to the parameters of caller. If there's an - in-charge parameter, map it to an appropriate constant. */ + parameters in the callee to the parameters of caller. */ memset (&id, 0, sizeof (id)); - VARRAY_TREE_INIT (id.fns, 2, "fns"); - VARRAY_PUSH_TREE (id.fns, clone); - VARRAY_PUSH_TREE (id.fns, fn); + id.caller = clone; + id.callee = fn; + id.callee_cfun = DECL_STRUCT_FUNCTION (fn); id.decl_map = (splay_tree)arg_map; /* Cloning is treated slightly differently from inlining. Set CLONING_P so that it's clear which operation we're performing. */ id.cloning_p = true; + /* We're not inside any EH region. */ + id.eh_region = -1; + /* Actually copy the body. */ - append_to_statement_list_force (copy_body (&id), &DECL_SAVED_TREE (clone)); + append_to_statement_list_force (copy_generic_body (&id), &DECL_SAVED_TREE (clone)); } +/* Save duplicate body in FN. MAP is used to pass around splay tree + used to update arguments in restore_body. */ + /* Make and return duplicate of body in FN. Put copies of DECL_ARGUMENTS in *arg_copy and of the static chain, if any, in *sc_copy. */ -tree +void save_body (tree fn, tree *arg_copy, tree *sc_copy) { inline_data id; - tree body, *parg; + tree newdecl, *parg; + basic_block fn_entry_block; + tree t_step; memset (&id, 0, sizeof (id)); - VARRAY_TREE_INIT (id.fns, 1, "fns"); - VARRAY_PUSH_TREE (id.fns, fn); + id.callee = fn; + id.callee_cfun = DECL_STRUCT_FUNCTION (fn); + id.caller = fn; id.node = cgraph_node (fn); id.saving_p = true; id.decl_map = splay_tree_new (splay_tree_compare_pointers, NULL, NULL); @@ -1889,364 +2376,38 @@ save_body (tree fn, tree *arg_copy, tree *sc_copy) *sc_copy = new; } - insert_decl_map (&id, DECL_RESULT (fn), DECL_RESULT (fn)); - - /* Actually copy the body. */ - body = copy_body (&id); - - /* Clean up. */ - splay_tree_delete (id.decl_map); - return body; -} - -#define WALK_SUBTREE(NODE) \ - do \ - { \ - result = walk_tree (&(NODE), func, data, pset); \ - if (result) \ - return result; \ - } \ - while (0) - -/* This is a subroutine of walk_tree that walks field of TYPE that are to - be walked whenever a type is seen in the tree. Rest of operands and return - value are as for walk_tree. */ - -static tree -walk_type_fields (tree type, walk_tree_fn func, void *data, - struct pointer_set_t *pset) -{ - tree result = NULL_TREE; - - switch (TREE_CODE (type)) - { - case POINTER_TYPE: - case REFERENCE_TYPE: - /* We have to worry about mutually recursive pointers. These can't - be written in C. They can in Ada. It's pathological, but - there's an ACATS test (c38102a) that checks it. Deal with this - by checking if we're pointing to another pointer, that one - points to another pointer, that one does too, and we have no htab. - If so, get a hash table. We check three levels deep to avoid - the cost of the hash table if we don't need one. */ - if (POINTER_TYPE_P (TREE_TYPE (type)) - && POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (type))) - && POINTER_TYPE_P (TREE_TYPE (TREE_TYPE (TREE_TYPE (type)))) - && !pset) - { - result = walk_tree_without_duplicates (&TREE_TYPE (type), - func, data); - if (result) - return result; - - break; - } - - /* ... fall through ... */ - - case COMPLEX_TYPE: - WALK_SUBTREE (TREE_TYPE (type)); - break; - - case METHOD_TYPE: - WALK_SUBTREE (TYPE_METHOD_BASETYPE (type)); - - /* Fall through. */ - - case FUNCTION_TYPE: - WALK_SUBTREE (TREE_TYPE (type)); - { - tree arg; - - /* We never want to walk into default arguments. */ - for (arg = TYPE_ARG_TYPES (type); arg; arg = TREE_CHAIN (arg)) - WALK_SUBTREE (TREE_VALUE (arg)); - } - break; - - case ARRAY_TYPE: - /* Don't follow this nodes's type if a pointer for fear that we'll - have infinite recursion. Those types are uninteresting anyway. */ - if (!POINTER_TYPE_P (TREE_TYPE (type)) - && TREE_CODE (TREE_TYPE (type)) != OFFSET_TYPE) - WALK_SUBTREE (TREE_TYPE (type)); - WALK_SUBTREE (TYPE_DOMAIN (type)); - break; - - case BOOLEAN_TYPE: - case ENUMERAL_TYPE: - case INTEGER_TYPE: - case CHAR_TYPE: - case REAL_TYPE: - WALK_SUBTREE (TYPE_MIN_VALUE (type)); - WALK_SUBTREE (TYPE_MAX_VALUE (type)); - break; - - case OFFSET_TYPE: - WALK_SUBTREE (TREE_TYPE (type)); - WALK_SUBTREE (TYPE_OFFSET_BASETYPE (type)); - break; - - default: - break; - } - - return NULL_TREE; -} - -/* Apply FUNC to all the sub-trees of TP in a pre-order traversal. FUNC is - called with the DATA and the address of each sub-tree. If FUNC returns a - non-NULL value, the traversal is aborted, and the value returned by FUNC - is returned. If PSET is non-NULL it is used to record the nodes visited, - and to avoid visiting a node more than once. */ - -tree -walk_tree (tree *tp, walk_tree_fn func, void *data, struct pointer_set_t *pset) -{ - enum tree_code code; - int walk_subtrees; - tree result; - -#define WALK_SUBTREE_TAIL(NODE) \ - do \ - { \ - tp = & (NODE); \ - goto tail_recurse; \ - } \ - while (0) - - tail_recurse: - /* Skip empty subtrees. */ - if (!*tp) - return NULL_TREE; - - /* Don't walk the same tree twice, if the user has requested - that we avoid doing so. */ - if (pset && pointer_set_insert (pset, *tp)) - return NULL_TREE; - - /* Call the function. */ - walk_subtrees = 1; - result = (*func) (tp, &walk_subtrees, data); - - /* If we found something, return it. */ - if (result) - return result; - - code = TREE_CODE (*tp); - - /* Even if we didn't, FUNC may have decided that there was nothing - interesting below this point in the tree. */ - if (!walk_subtrees) - { - if (code == TREE_LIST) - /* But we still need to check our siblings. */ - WALK_SUBTREE_TAIL (TREE_CHAIN (*tp)); - else - return NULL_TREE; - } - - result = lang_hooks.tree_inlining.walk_subtrees (tp, &walk_subtrees, func, - data, pset); - if (result || ! walk_subtrees) - return result; - - /* If this is a DECL_EXPR, walk into various fields of the type that it's - defining. We only want to walk into these fields of a type in this - case. Note that decls get walked as part of the processing of a - BIND_EXPR. - - ??? Precisely which fields of types that we are supposed to walk in - this case vs. the normal case aren't well defined. */ - if (code == DECL_EXPR - && TREE_CODE (DECL_EXPR_DECL (*tp)) == TYPE_DECL - && TREE_CODE (TREE_TYPE (DECL_EXPR_DECL (*tp))) != ERROR_MARK) - { - tree *type_p = &TREE_TYPE (DECL_EXPR_DECL (*tp)); - - /* Call the function for the type. See if it returns anything or - doesn't want us to continue. If we are to continue, walk both - the normal fields and those for the declaration case. */ - result = (*func) (type_p, &walk_subtrees, data); - if (result || !walk_subtrees) - return NULL_TREE; - - result = walk_type_fields (*type_p, func, data, pset); - if (result) - return result; - - WALK_SUBTREE (TYPE_SIZE (*type_p)); - WALK_SUBTREE (TYPE_SIZE_UNIT (*type_p)); - - /* If this is a record type, also walk the fields. */ - if (TREE_CODE (*type_p) == RECORD_TYPE - || TREE_CODE (*type_p) == UNION_TYPE - || TREE_CODE (*type_p) == QUAL_UNION_TYPE) - { - tree field; - - for (field = TYPE_FIELDS (*type_p); field; - field = TREE_CHAIN (field)) - { - /* We'd like to look at the type of the field, but we can easily - get infinite recursion. So assume it's pointed to elsewhere - in the tree. Also, ignore things that aren't fields. */ - if (TREE_CODE (field) != FIELD_DECL) - continue; - - WALK_SUBTREE (DECL_FIELD_OFFSET (field)); - WALK_SUBTREE (DECL_SIZE (field)); - WALK_SUBTREE (DECL_SIZE_UNIT (field)); - if (TREE_CODE (*type_p) == QUAL_UNION_TYPE) - WALK_SUBTREE (DECL_QUALIFIER (field)); - } - } - } + /* We're not inside any EH region. */ + id.eh_region = -1; - else if (code != SAVE_EXPR - && code != BIND_EXPR - && IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (code))) - { - int i, len; - - /* Walk over all the sub-trees of this operand. */ - len = TREE_CODE_LENGTH (code); - /* TARGET_EXPRs are peculiar: operands 1 and 3 can be the same. - But, we only want to walk once. */ - if (code == TARGET_EXPR - && TREE_OPERAND (*tp, 3) == TREE_OPERAND (*tp, 1)) - --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)); - - if (len) - { - /* 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 - } + insert_decl_map (&id, DECL_RESULT (fn), DECL_RESULT (fn)); - /* If this is a type, walk the needed fields in the type. */ - else if (TYPE_P (*tp)) + DECL_STRUCT_FUNCTION (fn)->saved_blocks + = remap_blocks (DECL_INITIAL (fn), &id); + for (t_step = id.callee_cfun->unexpanded_var_list; + t_step; + t_step = TREE_CHAIN (t_step)) { - result = walk_type_fields (*tp, func, data, pset); - if (result) - return result; - } - else - { - /* 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 BLOCK: - case PLACEHOLDER_EXPR: - case SSA_NAME: - case FIELD_DECL: - case RESULT_DECL: - /* None of thse have subtrees other than those already walked - above. */ - 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); - - if (len == 0) - break; - - /* 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)); - } - - case COMPLEX_CST: - WALK_SUBTREE (TREE_REALPART (*tp)); - WALK_SUBTREE_TAIL (TREE_IMAGPART (*tp)); - - case CONSTRUCTOR: - WALK_SUBTREE_TAIL (CONSTRUCTOR_ELTS (*tp)); - - case SAVE_EXPR: - WALK_SUBTREE_TAIL (TREE_OPERAND (*tp, 0)); - - 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_TAIL (BIND_EXPR_BODY (*tp)); - } - - 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: - /* ??? This could be a language-defined node. We really should make - a hook for it, but right now just ignore it. */ - break; - } + tree var = TREE_VALUE (t_step); + if (TREE_STATIC (var) && !TREE_ASM_WRITTEN (var)) + cfun->saved_unexpanded_var_list + = tree_cons (NULL_TREE, var, cfun->saved_unexpanded_var_list); + else + cfun->saved_unexpanded_var_list + = tree_cons (NULL_TREE, remap_decl (var, &id), + cfun->saved_unexpanded_var_list); } - /* We didn't find what we were looking for. */ - return NULL_TREE; - -#undef WALK_SUBTREE -#undef WALK_SUBTREE_TAIL -} + /* Actually copy the body, including a new (struct function *) and CFG. + EH info is also duplicated so its labels point into the copied + CFG, not the original. */ + fn_entry_block = ENTRY_BLOCK_PTR_FOR_FUNCTION (DECL_STRUCT_FUNCTION (fn)); + newdecl = copy_body (&id, fn_entry_block->count, fn_entry_block->frequency, + NULL, NULL); + DECL_STRUCT_FUNCTION (fn)->saved_cfg = DECL_STRUCT_FUNCTION (newdecl)->cfg; + DECL_STRUCT_FUNCTION (fn)->saved_eh = DECL_STRUCT_FUNCTION (newdecl)->eh; -/* Like walk_tree, but does not walk duplicate nodes more than once. */ - -tree -walk_tree_without_duplicates (tree *tp, walk_tree_fn func, void *data) -{ - tree result; - struct pointer_set_t *pset; - - pset = pointer_set_create (); - result = walk_tree (tp, func, data, pset); - pointer_set_destroy (pset); - return result; + /* Clean up. */ + splay_tree_delete (id.decl_map); } /* Passed to walk_tree. Copies the node pointed to, if appropriate. */ @@ -2255,6 +2416,7 @@ tree copy_tree_r (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED) { enum tree_code code = TREE_CODE (*tp); + inline_data *id = (inline_data *) data; /* We make copies of most nodes. */ if (IS_EXPR_CODE_CLASS (TREE_CODE_CLASS (code)) @@ -2267,6 +2429,11 @@ copy_tree_r (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED) tree chain = TREE_CHAIN (*tp); tree new; + if (id && id->versioning_p && replace_ref_tree (id, tp)) + { + *walk_subtrees = 0; + return NULL_TREE; + } /* Copy the node. */ new = copy_node (*tp); @@ -2286,7 +2453,22 @@ copy_tree_r (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED) if (TREE_CODE (*tp) == BIND_EXPR) BIND_EXPR_BLOCK (*tp) = NULL_TREE; } + else if (code == CONSTRUCTOR) + { + /* CONSTRUCTOR nodes need special handling because + we need to duplicate the vector of elements. */ + tree new; + + new = copy_node (*tp); + + /* Propagate mudflap marked-ness. */ + if (flag_mudflap && mf_marked_p (*tp)) + mf_mark (new); + CONSTRUCTOR_ELTS (new) = VEC_copy (constructor_elt, gc, + CONSTRUCTOR_ELTS (*tp)); + *tp = new; + } else if (TREE_CODE_CLASS (code) == tcc_type) *walk_subtrees = 0; else if (TREE_CODE_CLASS (code) == tcc_declaration) @@ -2300,7 +2482,8 @@ copy_tree_r (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED) /* The SAVE_EXPR pointed to by TP is being copied. If ST contains information indicating to what new SAVE_EXPR this one should be mapped, - use that one. Otherwise, create a new node and enter it in ST. */ + use that one. Otherwise, create a new node and enter it in ST. FN is + the function into which the copy will be placed. */ static void remap_save_expr (tree *tp, void *st_, int *walk_subtrees) @@ -2353,8 +2536,8 @@ mark_local_for_remap_r (tree *tp, int *walk_subtrees ATTRIBUTE_UNUSED, /* Copy the decl and remember the copy. */ insert_decl_map (id, decl, - copy_decl_for_inlining (decl, DECL_CONTEXT (decl), - DECL_CONTEXT (decl))); + copy_decl_for_dup (decl, DECL_CONTEXT (decl), + DECL_CONTEXT (decl), /*versioning=*/false)); } return NULL_TREE; @@ -2440,8 +2623,8 @@ unsave_expr_now (tree 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.callee = current_function_decl; + id.caller = current_function_decl; id.decl_map = splay_tree_new (splay_tree_compare_pointers, NULL, NULL); /* Walk the tree once to find local labels. */ @@ -2473,15 +2656,350 @@ 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) +declare_inline_vars (tree block, tree vars) { tree t; for (t = vars; t; t = TREE_CHAIN (t)) DECL_SEEN_IN_BIND_EXPR_P (t) = 1; - add_var_to_bind_expr (bind_expr, vars); + if (block) + BLOCK_VARS (block) = chainon (BLOCK_VARS (block), vars); +} + + +/* Copy NODE (which must be a DECL). The DECL originally was in the FROM_FN, + but now it will be in the TO_FN. VERSIONING means that this function + is used by the versioning utility (not inlining or cloning). */ + +tree +copy_decl_for_dup (tree decl, tree from_fn, tree to_fn, bool versioning) +{ + tree copy; + + gcc_assert (DECL_P (decl)); + /* Copy the declaration. */ + if (!versioning + && (TREE_CODE (decl) == PARM_DECL + || TREE_CODE (decl) == RESULT_DECL)) + { + tree type = TREE_TYPE (decl); + + /* For a parameter or result, we must make an equivalent VAR_DECL, + not a new PARM_DECL. */ + copy = build_decl (VAR_DECL, DECL_NAME (decl), type); + TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (decl); + TREE_READONLY (copy) = TREE_READONLY (decl); + TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (decl); + DECL_COMPLEX_GIMPLE_REG_P (copy) = DECL_COMPLEX_GIMPLE_REG_P (decl); + } + else + { + copy = copy_node (decl); + /* The COPY is not abstract; it will be generated in TO_FN. */ + DECL_ABSTRACT (copy) = 0; + lang_hooks.dup_lang_specific_decl (copy); + + /* TREE_ADDRESSABLE isn't used to indicate that a label's + address has been taken; it's for internal bookkeeping in + expand_goto_internal. */ + if (TREE_CODE (copy) == LABEL_DECL) + { + TREE_ADDRESSABLE (copy) = 0; + LABEL_DECL_UID (copy) = -1; + } + } + + /* Don't generate debug information for the copy if we wouldn't have + generated it for the copy either. */ + DECL_ARTIFICIAL (copy) = DECL_ARTIFICIAL (decl); + DECL_IGNORED_P (copy) = DECL_IGNORED_P (decl); + + /* Set the DECL_ABSTRACT_ORIGIN so the debugging routines know what + declaration inspired this copy. */ + DECL_ABSTRACT_ORIGIN (copy) = DECL_ORIGIN (decl); + + /* The new variable/label has no RTL, yet. */ + if (CODE_CONTAINS_STRUCT (TREE_CODE (copy), TS_DECL_WRTL) + && !TREE_STATIC (copy) && !DECL_EXTERNAL (copy)) + SET_DECL_RTL (copy, NULL_RTX); + + /* These args would always appear unused, if not for this. */ + TREE_USED (copy) = 1; + + /* Set the context for the new declaration. */ + if (!DECL_CONTEXT (decl)) + /* Globals stay global. */ + ; + else if (DECL_CONTEXT (decl) != from_fn) + /* Things that weren't in the scope of the function we're inlining + from aren't in the scope we're inlining to, either. */ + ; + else if (TREE_STATIC (decl)) + /* Function-scoped static variables should stay in the original + function. */ + ; + else + /* Ordinary automatic local variables are now in the scope of the + new function. */ + DECL_CONTEXT (copy) = to_fn; + + return copy; +} + +/* Return a copy of the function's argument tree. */ +static tree +copy_arguments_for_versioning (tree orig_parm, inline_data * id) +{ + tree *arg_copy, *parg; + + arg_copy = &orig_parm; + for (parg = arg_copy; *parg; parg = &TREE_CHAIN (*parg)) + { + tree new = remap_decl (*parg, id); + lang_hooks.dup_lang_specific_decl (new); + TREE_CHAIN (new) = TREE_CHAIN (*parg); + *parg = new; + } + return orig_parm; +} + +/* Return a copy of the function's static chain. */ +static tree +copy_static_chain (tree static_chain, inline_data * id) +{ + tree *chain_copy, *pvar; + + chain_copy = &static_chain; + for (pvar = chain_copy; *pvar; pvar = &TREE_CHAIN (*pvar)) + { + tree new = remap_decl (*pvar, id); + lang_hooks.dup_lang_specific_decl (new); + TREE_CHAIN (new) = TREE_CHAIN (*pvar); + *pvar = new; + } + return static_chain; +} + +/* Return true if the function is allowed to be versioned. + This is a guard for the versioning functionality. */ +bool +tree_versionable_function_p (tree fndecl) +{ + if (fndecl == NULL_TREE) + return false; + /* ??? There are cases where a function is + uninlinable but can be versioned. */ + if (!tree_inlinable_function_p (fndecl)) + return false; + + return true; +} + +/* Create a copy of a function's tree. + OLD_DECL and NEW_DECL are FUNCTION_DECL tree nodes + of the original function and the new copied function + respectively. In case we want to replace a DECL + tree with another tree while duplicating the function's + body, TREE_MAP represents the mapping between these + trees. */ +void +tree_function_versioning (tree old_decl, tree new_decl, varray_type tree_map) +{ + struct cgraph_node *old_version_node; + struct cgraph_node *new_version_node; + inline_data id; + tree p, new_fndecl; + unsigned i; + struct ipa_replace_map *replace_info; + basic_block old_entry_block; + tree t_step; + + gcc_assert (TREE_CODE (old_decl) == FUNCTION_DECL + && TREE_CODE (new_decl) == FUNCTION_DECL); + DECL_POSSIBLY_INLINED (old_decl) = 1; + + old_version_node = cgraph_node (old_decl); + new_version_node = cgraph_node (new_decl); + + allocate_struct_function (new_decl); + /* Cfun points to the new allocated function struct at this point. */ + cfun->function_end_locus = DECL_SOURCE_LOCATION (new_decl); + + DECL_ARTIFICIAL (new_decl) = 1; + DECL_ABSTRACT_ORIGIN (new_decl) = DECL_ORIGIN (old_decl); + + /* Generate a new name for the new version. */ + DECL_NAME (new_decl) = + create_tmp_var_name (NULL); + /* Create a new SYMBOL_REF rtx for the new name. */ + if (DECL_RTL (old_decl) != NULL) + { + SET_DECL_RTL (new_decl, copy_rtx (DECL_RTL (old_decl))); + XEXP (DECL_RTL (new_decl), 0) = + gen_rtx_SYMBOL_REF (GET_MODE (XEXP (DECL_RTL (old_decl), 0)), + IDENTIFIER_POINTER (DECL_NAME (new_decl))); + } + + /* Prepare the data structures for the tree copy. */ + memset (&id, 0, sizeof (id)); + + /* The new version. */ + id.node = new_version_node; + + /* The old version. */ + id.current_node = cgraph_node (old_decl); + + id.versioning_p = true; + id.decl_map = splay_tree_new (splay_tree_compare_pointers, NULL, NULL); + id.caller = new_decl; + id.callee = old_decl; + id.callee_cfun = DECL_STRUCT_FUNCTION (old_decl); + + current_function_decl = new_decl; + + /* Copy the function's static chain. */ + p = DECL_STRUCT_FUNCTION (old_decl)->static_chain_decl; + if (p) + DECL_STRUCT_FUNCTION (new_decl)->static_chain_decl = + copy_static_chain (DECL_STRUCT_FUNCTION (old_decl)->static_chain_decl, + &id); + /* Copy the function's arguments. */ + if (DECL_ARGUMENTS (old_decl) != NULL_TREE) + DECL_ARGUMENTS (new_decl) = + copy_arguments_for_versioning (DECL_ARGUMENTS (old_decl), &id); + + /* If there's a tree_map, prepare for substitution. */ + if (tree_map) + for (i = 0; i < VARRAY_ACTIVE_SIZE (tree_map); i++) + { + replace_info = VARRAY_GENERIC_PTR (tree_map, i); + if (replace_info->replace_p && !replace_info->ref_p) + insert_decl_map (&id, replace_info->old_tree, + replace_info->new_tree); + else if (replace_info->replace_p && replace_info->ref_p) + id.ipa_info = tree_map; + } + + DECL_INITIAL (new_decl) = remap_blocks (DECL_INITIAL (id.callee), &id); + + /* Renumber the lexical scoping (non-code) blocks consecutively. */ + number_blocks (id.caller); + + if (DECL_STRUCT_FUNCTION (old_decl)->unexpanded_var_list != NULL_TREE) + /* Add local vars. */ + for (t_step = DECL_STRUCT_FUNCTION (old_decl)->unexpanded_var_list; + t_step; t_step = TREE_CHAIN (t_step)) + { + tree var = TREE_VALUE (t_step); + if (TREE_STATIC (var) && !TREE_ASM_WRITTEN (var)) + cfun->unexpanded_var_list = tree_cons (NULL_TREE, var, + cfun->unexpanded_var_list); + else + cfun->unexpanded_var_list = + tree_cons (NULL_TREE, remap_decl (var, &id), + cfun->unexpanded_var_list); + } + + /* Copy the Function's body. */ + old_entry_block = ENTRY_BLOCK_PTR_FOR_FUNCTION + (DECL_STRUCT_FUNCTION (old_decl)); + new_fndecl = copy_body (&id, + old_entry_block->count, + old_entry_block->frequency, NULL, NULL); + + DECL_SAVED_TREE (new_decl) = DECL_SAVED_TREE (new_fndecl); + + DECL_STRUCT_FUNCTION (new_decl)->cfg = + DECL_STRUCT_FUNCTION (new_fndecl)->cfg; + DECL_STRUCT_FUNCTION (new_decl)->eh = DECL_STRUCT_FUNCTION (new_fndecl)->eh; + DECL_STRUCT_FUNCTION (new_decl)->ib_boundaries_block = + DECL_STRUCT_FUNCTION (new_fndecl)->ib_boundaries_block; + DECL_STRUCT_FUNCTION (new_decl)->last_label_uid = + DECL_STRUCT_FUNCTION (new_fndecl)->last_label_uid; + + if (DECL_RESULT (old_decl) != NULL_TREE) + { + tree *res_decl = &DECL_RESULT (old_decl); + DECL_RESULT (new_decl) = remap_decl (*res_decl, &id); + lang_hooks.dup_lang_specific_decl (DECL_RESULT (new_decl)); + } + + current_function_decl = NULL; + /* Renumber the lexical scoping (non-code) blocks consecutively. */ + number_blocks (new_decl); + + /* Clean up. */ + splay_tree_delete (id.decl_map); + fold_cond_expr_cond (); + return; +} + +/* Replace an INDIRECT_REF tree of a given DECL tree with a new + given tree. + ID->ipa_info keeps the old tree and the new tree. + TP points to the INDIRECT REF tree. Return true if + the trees were replaced. */ +static bool +replace_ref_tree (inline_data * id, tree * tp) +{ + bool replaced = false; + tree new; + + if (id->ipa_info && VARRAY_ACTIVE_SIZE (id->ipa_info) > 0) + { + unsigned i; + + for (i = 0; i < VARRAY_ACTIVE_SIZE (id->ipa_info); i++) + { + struct ipa_replace_map *replace_info; + replace_info = VARRAY_GENERIC_PTR (id->ipa_info, i); + + if (replace_info->replace_p && replace_info->ref_p) + { + tree old_tree = replace_info->old_tree; + tree new_tree = replace_info->new_tree; + + if (TREE_CODE (*tp) == INDIRECT_REF + && TREE_OPERAND (*tp, 0) == old_tree) + { + new = copy_node (new_tree); + *tp = new; + replaced = true; + } + } + } + } + return replaced; +} + +/* Return true if we are inlining. */ +static inline bool +inlining_p (inline_data * id) +{ + return (!id->saving_p && !id->cloning_p && !id->versioning_p); +} + +/* Duplicate a type, fields and all. */ + +tree +build_duplicate_type (tree type) +{ + inline_data id; + + memset (&id, 0, sizeof (id)); + id.callee = current_function_decl; + id.caller = current_function_decl; + id.callee_cfun = cfun; + id.decl_map = splay_tree_new (splay_tree_compare_pointers, NULL, NULL); + + type = remap_type_1 (type, &id); + + splay_tree_delete (id.decl_map); + + return type; }