diff options
author | razya <razya@138bc75d-0d04-0410-961f-82ee72b054a4> | 2005-08-01 07:47:25 +0000 |
---|---|---|
committer | razya <razya@138bc75d-0d04-0410-961f-82ee72b054a4> | 2005-08-01 07:47:25 +0000 |
commit | c5235c0b315ea8765f6b018a98fee695d60d9469 (patch) | |
tree | 4da9fbc1ee7db5362ec4694645f81df848322798 /gcc | |
parent | b5d36404491f8d956d5061823e212a6ed4cfed50 (diff) | |
download | gcc-c5235c0b315ea8765f6b018a98fee695d60d9469.tar.gz |
Interprocedural constant propagation.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@102626 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/timevar.def | 1 | ||||
-rw-r--r-- | gcc/tree-inline.c | 365 | ||||
-rw-r--r-- | gcc/tree-inline.h | 7 | ||||
-rw-r--r-- | gcc/tree-pass.h | 1 |
4 files changed, 355 insertions, 19 deletions
diff --git a/gcc/timevar.def b/gcc/timevar.def index 05d8b386246..38e18031fd3 100644 --- a/gcc/timevar.def +++ b/gcc/timevar.def @@ -42,6 +42,7 @@ DEFTIMEVAR (TV_DUMP , "dump files") DEFTIMEVAR (TV_CGRAPH , "callgraph construction") DEFTIMEVAR (TV_CGRAPHOPT , "callgraph optimization") +DEFTIMEVAR (TV_IPA_CONSTANT_PROP , "ipa cp") DEFTIMEVAR (TV_IPA_REFERENCE , "ipa reference") DEFTIMEVAR (TV_IPA_PURE_CONST , "ipa pure const") DEFTIMEVAR (TV_IPA_TYPE_ESCAPE , "ipa type escape") diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c index fa6970362e2..9059b1a9a52 100644 --- a/gcc/tree-inline.c +++ b/gcc/tree-inline.c @@ -49,7 +49,7 @@ Boston, MA 02110-1301, USA. */ #include "except.h" #include "debug.h" #include "pointer-set.h" -#include "integrate.h" +#include "ipa-prop.h" /* I'm not real happy about this, but we need to handle gimple and non-gimple trees. */ @@ -127,12 +127,15 @@ typedef struct inline_data bool cloning_p; /* Similarly for saving function body. */ bool saving_p; + /* 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; /* 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 @@ -157,8 +160,8 @@ static void unsave_expr_1 (tree); static tree unsave_r (tree *, int *, void *); static void declare_inline_vars (tree, tree); static void remap_save_expr (tree *, void *, int *); - -static inline bool inlining_p (inline_data *id); +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 @@ -198,8 +201,8 @@ remap_decl (tree decl, inline_data *id) { /* Make a copy of the variable or label. */ tree t; - t = copy_decl_for_inlining (decl, fn, id->caller); - + 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. */ @@ -607,7 +610,8 @@ 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. */ @@ -639,8 +643,8 @@ copy_body_r (tree *tp, int *walk_subtrees, void *data) /* Here is the "usual case". Copy this tree node, and then tweak some special cases. */ - copy_tree_r (tp, walk_subtrees, NULL); - + 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. */ @@ -749,10 +753,22 @@ copy_bb (inline_data *id, basic_block bb, int frequency_scale, int count_scale) /* We're cloning or inlining this body; duplicate the associate callgraph nodes. */ - edge = cgraph_edge (id->current_node, orig_stmt); + 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) - cgraph_clone_edge (edge, id->node, stmt, - REG_BR_PROB_BASE, 1, true); + edge->call_stmt = stmt; } } /* If you think we can abort here, you are wrong. @@ -921,7 +937,7 @@ copy_cfg_body (inline_data * id, gcov_type count, int frequency, 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); + saving_or_cloning = (id->saving_p || id->cloning_p || id->versioning_p); if (saving_or_cloning) { new_cfun = @@ -1061,7 +1077,7 @@ 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, id->caller); + 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 @@ -1259,7 +1275,7 @@ 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 @@ -2365,6 +2381,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)) @@ -2377,6 +2394,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); @@ -2479,8 +2501,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; @@ -2614,9 +2636,314 @@ declare_inline_vars (tree block, tree vars) BLOCK_VARS (block) = chainon (BLOCK_VARS (block), vars); } -/* Returns true if we're inlining. */ + +/* 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 (!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) +inlining_p (inline_data * id) { - return (!id->saving_p && !id->cloning_p); + return (!id->saving_p && !id->cloning_p && !id->versioning_p); } diff --git a/gcc/tree-inline.h b/gcc/tree-inline.h index 23d562cc7d1..65ffed29b8d 100644 --- a/gcc/tree-inline.h +++ b/gcc/tree-inline.h @@ -22,6 +22,7 @@ Boston, MA 02110-1301, USA. */ #ifndef GCC_TREE_INLINE_H #define GCC_TREE_INLINE_H +#include "varray.h" /* Function prototypes. */ void optimize_inline_calls (tree); @@ -33,6 +34,12 @@ int estimate_move_cost (tree type); void push_cfun (struct function *new_cfun); void pop_cfun (void); int estimate_num_insns (tree expr); +bool tree_versionable_function_p (tree); +void tree_function_versioning (tree, tree, varray_type); + +/* Copy a declaration when one function is substituted inline into + another. It is used also for versioning. */ +extern tree copy_decl_for_dup (tree, tree, tree, bool); /* 0 if we should not perform inlining. 1 if we should expand functions calls inline at the tree level. diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index a51054df246..36b122c6127 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -284,6 +284,7 @@ extern struct tree_opt_pass pass_rebuild_cgraph_edges; extern struct tree_opt_pass pass_eliminate_useless_stores; /* IPA Passes */ +extern struct tree_opt_pass pass_ipa_cp; extern struct tree_opt_pass pass_ipa_inline; extern struct tree_opt_pass pass_early_ipa_inline; extern struct tree_opt_pass pass_ipa_reference; |