diff options
author | Jan Hubicka <jh@suse.cz> | 2008-08-29 13:31:40 +0200 |
---|---|---|
committer | Jan Hubicka <hubicka@gcc.gnu.org> | 2008-08-29 11:31:40 +0000 |
commit | c6f7cfc15e7480f86aa3e4d407932f38fd635c89 (patch) | |
tree | d057c22936747f4672fd349b48d59df2628ab832 | |
parent | efd8f7507b3ce6e4cc00c7eac4f011736ca4f14d (diff) | |
download | gcc-c6f7cfc15e7480f86aa3e4d407932f38fd635c89.zip gcc-c6f7cfc15e7480f86aa3e4d407932f38fd635c89.tar.gz gcc-c6f7cfc15e7480f86aa3e4d407932f38fd635c89.tar.bz2 |
tree.c (build_function_type_skip_args, [...]): New functions.
* tree.c (build_function_type_skip_args, build_function_decl_skip_args):
New functions.
* tree.h (build_function_type_skip_args, build_function_decl_skip_args):
Declare.
* gimple.c (giple_copy_call_skip_args): New function.
(giple_copy_call_skip_args): Declare.
* cgraph.h (cgraph_function_versioning): Add skip_args arugmnet
* ipa-cp.c (ipcp_node_not_modifiable_p): Rename to ...
(ipcp_node_modifiable_p): ... this one; use tree_versionable_function_p.
(ipcp_create_replace_map): Improve debug output.
(ipcp_need_redirect_p): Return false when not clonning.
(ipcp_update_callgraph): Skip args.
(ipcp_insert_stage): UPdate call of !ipcp_node_modifiable_p;
skip args.
* cgraphunit.c (cgraph_function_versioning): Add skip_args argument.
(save_inline_function_body): Update call of tree_function_versioning.
* ipa-prop.c (ipa_edge_removal_hook): Do not ICE on unanalyzed nodes.
* tree-inline.c (copy_arguments_for_versioning): Add skip_args argument.
(tree_function_versioning): Likewise.
* tree-inline.h (tree_function_versioning): Update prototype.
From-SVN: r139761
-rw-r--r-- | gcc/ChangeLog | 24 | ||||
-rw-r--r-- | gcc/cgraph.h | 3 | ||||
-rw-r--r-- | gcc/cgraphunit.c | 16 | ||||
-rw-r--r-- | gcc/gimple.c | 33 | ||||
-rw-r--r-- | gcc/gimple.h | 1 | ||||
-rw-r--r-- | gcc/ipa-cp.c | 107 | ||||
-rw-r--r-- | gcc/ipa-prop.c | 4 | ||||
-rw-r--r-- | gcc/tree-inline.c | 45 | ||||
-rw-r--r-- | gcc/tree-inline.h | 2 | ||||
-rw-r--r-- | gcc/tree.c | 81 | ||||
-rw-r--r-- | gcc/tree.h | 2 |
11 files changed, 267 insertions, 51 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 637a89e..c85ac79 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,5 +1,29 @@ 2008-08-29 Jan Hubicka <jh@suse.cz> + * tree.c (build_function_type_skip_args, build_function_decl_skip_args): + New functions. + * tree.h (build_function_type_skip_args, build_function_decl_skip_args): + Declare. + * gimple.c (giple_copy_call_skip_args): New function. + (giple_copy_call_skip_args): Declare. + + * cgraph.h (cgraph_function_versioning): Add skip_args arugmnet + * ipa-cp.c (ipcp_node_not_modifiable_p): Rename to ... + (ipcp_node_modifiable_p): ... this one; use tree_versionable_function_p. + (ipcp_create_replace_map): Improve debug output. + (ipcp_need_redirect_p): Return false when not clonning. + (ipcp_update_callgraph): Skip args. + (ipcp_insert_stage): UPdate call of !ipcp_node_modifiable_p; + skip args. + * cgraphunit.c (cgraph_function_versioning): Add skip_args argument. + (save_inline_function_body): Update call of tree_function_versioning. + * ipa-prop.c (ipa_edge_removal_hook): Do not ICE on unanalyzed nodes. + * tree-inline.c (copy_arguments_for_versioning): Add skip_args argument. + (tree_function_versioning): Likewise. + * tree-inline.h (tree_function_versioning): Update prototype. + +2008-08-29 Jan Hubicka <jh@suse.cz> + * loop-unswitch.c (unswitch_single_loop): Use optimize_loop_for_speed_p. * tree-ssa-threadupdate.c (mark_threaded_blocks): Use optimize_function_for_size_p. * tracer.c (ignore_bb_p): Use optimize_bb_for_size_p. diff --git a/gcc/cgraph.h b/gcc/cgraph.h index 15cbf29..5fe0eea 100644 --- a/gcc/cgraph.h +++ b/gcc/cgraph.h @@ -351,7 +351,8 @@ void cgraph_reset_static_var_maps (void); void init_cgraph (void); struct cgraph_node *cgraph_function_versioning (struct cgraph_node *, VEC(cgraph_edge_p,heap)*, - varray_type); + varray_type, + bitmap); void cgraph_analyze_function (struct cgraph_node *); struct cgraph_node *save_inline_function_body (struct cgraph_node *); void record_references_in_initializer (tree); diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c index 371e17c..43cdfda 100644 --- a/gcc/cgraphunit.c +++ b/gcc/cgraphunit.c @@ -1496,12 +1496,15 @@ cgraph_copy_node_for_versioning (struct cgraph_node *old_version, TREE_MAP is a mapping of tree nodes we want to replace with new ones (according to results of prior analysis). OLD_VERSION_NODE is the node that is versioned. - It returns the new version's cgraph node. */ + It returns the new version's cgraph node. + ARGS_TO_SKIP lists arguments to be omitted from functions + */ struct cgraph_node * cgraph_function_versioning (struct cgraph_node *old_version_node, VEC(cgraph_edge_p,heap) *redirect_callers, - varray_type tree_map) + varray_type tree_map, + bitmap args_to_skip) { tree old_decl = old_version_node->decl; struct cgraph_node *new_version_node = NULL; @@ -1512,7 +1515,10 @@ cgraph_function_versioning (struct cgraph_node *old_version_node, /* Make a new FUNCTION_DECL tree node for the new version. */ - new_decl = copy_node (old_decl); + if (!args_to_skip) + new_decl = copy_node (old_decl); + else + new_decl = build_function_decl_skip_args (old_decl, args_to_skip); /* Create the new version's call-graph node. and update the edges of the new node. */ @@ -1521,7 +1527,7 @@ cgraph_function_versioning (struct cgraph_node *old_version_node, redirect_callers); /* Copy the OLD_VERSION_NODE function tree to the new version. */ - tree_function_versioning (old_decl, new_decl, tree_map, false); + tree_function_versioning (old_decl, new_decl, tree_map, false, args_to_skip); /* Update the call_expr on the edges to call the new version node. */ update_call_expr (new_version_node); @@ -1560,7 +1566,7 @@ save_inline_function_body (struct cgraph_node *node) gcc_assert (first_clone == cgraph_node (first_clone->decl)); /* Copy the OLD_VERSION_NODE function tree to the new version. */ - tree_function_versioning (node->decl, first_clone->decl, NULL, true); + tree_function_versioning (node->decl, first_clone->decl, NULL, true, NULL); DECL_EXTERNAL (first_clone->decl) = 0; DECL_ONE_ONLY (first_clone->decl) = 0; diff --git a/gcc/gimple.c b/gcc/gimple.c index c651f0d..6e203b7 100644 --- a/gcc/gimple.c +++ b/gcc/gimple.c @@ -3180,4 +3180,37 @@ canonicalize_cond_expr_cond (tree t) return NULL_TREE; } +/* Build call same as STMT but skipping arguments ARGS_TO_SKIP. */ +gimple +giple_copy_call_skip_args (gimple stmt, bitmap args_to_skip) +{ + int i; + tree fn = gimple_call_fn (stmt); + int nargs = gimple_call_num_args (stmt); + VEC(tree, heap) *vargs = VEC_alloc (tree, heap, nargs); + gimple new_stmt; + + for (i = 0; i < nargs; i++) + if (!bitmap_bit_p (args_to_skip, i)) + VEC_quick_push (tree, vargs, gimple_call_arg (stmt, i)); + + new_stmt = gimple_build_call_vec (fn, vargs); + VEC_free (tree, heap, vargs); + if (gimple_call_lhs (stmt)) + gimple_call_set_lhs (new_stmt, gimple_call_lhs (stmt)); + + gimple_set_block (new_stmt, gimple_block (stmt)); + if (gimple_has_location (stmt)) + gimple_set_location (new_stmt, gimple_location (stmt)); + + /* Carry all the flags to the new GIMPLE_CALL. */ + gimple_call_set_chain (new_stmt, gimple_call_chain (stmt)); + gimple_call_set_tail (new_stmt, gimple_call_tail_p (stmt)); + gimple_call_set_cannot_inline (new_stmt, gimple_call_cannot_inline_p (stmt)); + gimple_call_set_return_slot_opt (new_stmt, gimple_call_return_slot_opt_p (stmt)); + gimple_call_set_from_thunk (new_stmt, gimple_call_from_thunk_p (stmt)); + gimple_call_set_va_arg_pack (new_stmt, gimple_call_va_arg_pack_p (stmt)); + return new_stmt; +} + #include "gt-gimple.h" diff --git a/gcc/gimple.h b/gcc/gimple.h index ebb8eeb..e8c0ad6 100644 --- a/gcc/gimple.h +++ b/gcc/gimple.h @@ -4471,6 +4471,7 @@ basic_block gsi_insert_on_edge_immediate (edge, gimple); basic_block gsi_insert_seq_on_edge_immediate (edge, gimple_seq); void gsi_commit_one_edge_insert (edge, basic_block *); void gsi_commit_edge_inserts (void); +gimple giple_copy_call_skip_args (gimple, bitmap); /* Convenience routines to walk all statements of a gimple function. diff --git a/gcc/ipa-cp.c b/gcc/ipa-cp.c index ca7e231..4b0a5f2 100644 --- a/gcc/ipa-cp.c +++ b/gcc/ipa-cp.c @@ -560,12 +560,11 @@ ipcp_iterate_stage (void) /* Check conditions to forbid constant insertion to function described by NODE. */ static inline bool -ipcp_node_not_modifiable_p (struct cgraph_node *node) +ipcp_node_modifiable_p (struct cgraph_node *node) { - /* ??? Handle pending sizes case. */ - if (DECL_UNINLINABLE (node->decl)) - return true; - return false; + /* Once we will be able to do in-place replacement, we can be more + lax here. */ + return tree_versionable_function_p (node->decl); } /* Print count scale data structures. */ @@ -745,9 +744,15 @@ ipcp_create_replace_map (tree parm_tree, struct ipcp_lattice *lat) tree const_val; replace_map = XCNEW (struct ipa_replace_map); - if (dump_file) - fprintf (dump_file, "replacing param with const\n"); const_val = build_const_val (lat, TREE_TYPE (parm_tree)); + if (dump_file) + { + fprintf (dump_file, " replacing param "); + print_generic_expr (dump_file, parm_tree, 0); + fprintf (dump_file, " with const "); + print_generic_expr (dump_file, const_val, 0); + fprintf (dump_file, "\n"); + } replace_map->old_tree = parm_tree; replace_map->new_tree = const_val; replace_map->replace_p = true; @@ -766,6 +771,9 @@ ipcp_need_redirect_p (struct cgraph_edge *cs) struct ipa_jump_func *jump_func; struct cgraph_node *node = cs->callee, *orig; + if (!flag_ipa_cp_clone) + return false; + if ((orig = ipcp_get_orig_node (node)) != NULL) node = orig; if (ipcp_get_orig_node (cs->caller)) @@ -791,26 +799,59 @@ ipcp_need_redirect_p (struct cgraph_edge *cs) static void ipcp_update_callgraph (void) { - struct cgraph_node *node, *orig_callee; - struct cgraph_edge *cs; + struct cgraph_node *node; for (node = cgraph_nodes; node; node = node->next) - { - /* want to fix only original nodes */ - if (!node->analyzed || ipcp_node_is_clone (node)) - continue; - for (cs = node->callees; cs; cs = cs->next_callee) - if (ipcp_node_is_clone (cs->callee)) + if (node->analyzed && ipcp_node_is_clone (node)) + { + bitmap args_to_skip = BITMAP_ALLOC (NULL); + struct cgraph_node *orig_node = ipcp_get_orig_node (node); + struct ipa_node_params *info = IPA_NODE_REF (orig_node); + int i, count = ipa_get_param_count (info); + struct cgraph_edge *cs, *next; + + for (i = 0; i < count; i++) { - /* Callee is a cloned node */ - orig_callee = ipcp_get_orig_node (cs->callee); - if (ipcp_need_redirect_p (cs)) + struct ipcp_lattice *lat = ipcp_get_ith_lattice (info, i); + tree parm_tree = ipa_get_ith_param (info, i); + + /* We can proactively remove obviously unused arguments. */ + if (is_gimple_reg (parm_tree) + && !gimple_default_def (DECL_STRUCT_FUNCTION (orig_node->decl), + parm_tree)) { - cgraph_redirect_edge_callee (cs, orig_callee); - gimple_call_set_fndecl (cs->call_stmt, orig_callee->decl); + bitmap_set_bit (args_to_skip, i); + continue; } + + if (lat->type == IPA_CONST_VALUE) + bitmap_set_bit (args_to_skip, i); } - } + for (cs = node->callers; cs; cs = next) + { + next = cs->next_caller; + if (ipcp_node_is_clone (cs->caller) || !ipcp_need_redirect_p (cs)) + { + gimple new_stmt; + gimple_stmt_iterator gsi; + + current_function_decl = cs->caller->decl; + push_cfun (DECL_STRUCT_FUNCTION (cs->caller->decl)); + + new_stmt = giple_copy_call_skip_args (cs->call_stmt, args_to_skip); + gsi = gsi_for_stmt (cs->call_stmt); + gsi_replace (&gsi, new_stmt, true); + cgraph_set_call_stmt (cs, new_stmt); + pop_cfun (); + current_function_decl = NULL; + } + else + { + cgraph_redirect_edge_callee (cs, orig_node); + gimple_call_set_fndecl (cs->call_stmt, orig_node->decl); + } + } + } } /* Update all cfg basic blocks in NODE according to SCALE. */ @@ -989,7 +1030,7 @@ ipcp_insert_stage (void) { struct ipa_node_params *info; /* Propagation of the constant is forbidden in certain conditions. */ - if (!node->analyzed || ipcp_node_not_modifiable_p (node)) + if (!node->analyzed || !ipcp_node_modifiable_p (node)) continue; info = IPA_NODE_REF (node); if (ipa_is_called_with_var_arguments (info)) @@ -1004,6 +1045,7 @@ ipcp_insert_stage (void) { struct ipa_node_params *info; int growth = 0; + bitmap args_to_skip; node = (struct cgraph_node *)fibheap_extract_min (heap); node->aux = NULL; @@ -1033,20 +1075,27 @@ ipcp_insert_stage (void) VARRAY_GENERIC_PTR_INIT (replace_trees, ipcp_const_param_count (node), "replace_trees"); + args_to_skip = BITMAP_ALLOC (NULL); for (i = 0; i < count; i++) { struct ipcp_lattice *lat = ipcp_get_ith_lattice (info, i); parm_tree = ipa_get_ith_param (info, i); - if (lat->type == IPA_CONST_VALUE - /* Do not count obviously unused arguments. */ - && (!is_gimple_reg (parm_tree) - || gimple_default_def (DECL_STRUCT_FUNCTION (node->decl), - parm_tree))) + /* We can proactively remove obviously unused arguments. */ + if (is_gimple_reg (parm_tree) + && !gimple_default_def (DECL_STRUCT_FUNCTION (node->decl), + parm_tree)) + { + bitmap_set_bit (args_to_skip, i); + continue; + } + + if (lat->type == IPA_CONST_VALUE) { replace_param = ipcp_create_replace_map (parm_tree, lat); VARRAY_PUSH_GENERIC_PTR (replace_trees, replace_param); + bitmap_set_bit (args_to_skip, i); } } @@ -1061,7 +1110,9 @@ ipcp_insert_stage (void) /* Redirecting all the callers of the node to the new versioned node. */ node1 = - cgraph_function_versioning (node, redirect_callers, replace_trees); + cgraph_function_versioning (node, redirect_callers, replace_trees, + args_to_skip); + BITMAP_FREE (args_to_skip); VEC_free (cgraph_edge_p, heap, redirect_callers); VARRAY_CLEAR (replace_trees); if (node1 == NULL) diff --git a/gcc/ipa-prop.c b/gcc/ipa-prop.c index 506a940..9a31b02 100644 --- a/gcc/ipa-prop.c +++ b/gcc/ipa-prop.c @@ -1057,6 +1057,10 @@ static void ipa_edge_removal_hook (struct cgraph_edge *cs, void *data __attribute__ ((unused))) { + /* During IPA-CP updating we can be called on not-yet analyze clones. */ + if (VEC_length (ipa_edge_args_t, ipa_edge_args_vector) + <= (unsigned)cs->uid) + return; ipa_free_edge_args_substructures (IPA_EDGE_REF (cs)); } diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c index 4fb28fe..eb95cc6 100644 --- a/gcc/tree-inline.c +++ b/gcc/tree-inline.c @@ -4089,19 +4089,37 @@ copy_decl_maybe_to_var (tree decl, copy_body_data *id) /* Return a copy of the function's argument tree. */ static tree -copy_arguments_for_versioning (tree orig_parm, copy_body_data * id) +copy_arguments_for_versioning (tree orig_parm, copy_body_data * id, + bitmap args_to_skip, tree *vars) { - tree *arg_copy, *parg; + tree arg, *parg; + tree new_parm = NULL; + int i = 0; - arg_copy = &orig_parm; - for (parg = arg_copy; *parg; parg = &TREE_CHAIN (*parg)) - { - tree new_tree = remap_decl (*parg, id); - lang_hooks.dup_lang_specific_decl (new_tree); - TREE_CHAIN (new_tree) = TREE_CHAIN (*parg); - *parg = new_tree; - } - return orig_parm; + parg = &new_parm; + + for (arg = orig_parm; arg; arg = TREE_CHAIN (arg), i++) + if (!args_to_skip || !bitmap_bit_p (args_to_skip, i)) + { + tree new_tree = remap_decl (arg, id); + lang_hooks.dup_lang_specific_decl (new_tree); + *parg = new_tree; + parg = &TREE_CHAIN (new_tree); + } + else + { + /* Make an equivalent VAR_DECL. If the argument was used + as temporary variable later in function, the uses will be + replaced by local variable. */ + tree var = copy_decl_to_var (arg, id); + get_var_ann (var); + add_referenced_var (var); + insert_decl_map (id, arg, var); + /* Declare this new variable. */ + TREE_CHAIN (var) = *vars; + *vars = var; + } + return new_parm; } /* Return a copy of the function's static chain. */ @@ -4146,7 +4164,7 @@ tree_versionable_function_p (tree fndecl) of edges of clones of the function will be updated. */ void tree_function_versioning (tree old_decl, tree new_decl, varray_type tree_map, - bool update_clones) + bool update_clones, bitmap args_to_skip) { struct cgraph_node *old_version_node; struct cgraph_node *new_version_node; @@ -4214,7 +4232,8 @@ tree_function_versioning (tree old_decl, tree new_decl, varray_type tree_map, /* 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); + copy_arguments_for_versioning (DECL_ARGUMENTS (old_decl), &id, + args_to_skip, &vars); DECL_INITIAL (new_decl) = remap_blocks (DECL_INITIAL (id.src_fn), &id); diff --git a/gcc/tree-inline.h b/gcc/tree-inline.h index e590e14..5fb4f63 100644 --- a/gcc/tree-inline.h +++ b/gcc/tree-inline.h @@ -156,7 +156,7 @@ int estimate_num_insns (gimple, eni_weights *); int estimate_num_insns_fn (tree, eni_weights *); int count_insns_seq (gimple_seq, eni_weights *); bool tree_versionable_function_p (tree); -void tree_function_versioning (tree, tree, varray_type, bool); +void tree_function_versioning (tree, tree, varray_type, bool, bitmap); bool tree_can_inline_p (tree, tree); extern gimple_seq remap_gimple_seq (gimple_seq, copy_body_data *); @@ -5878,6 +5878,81 @@ build_function_type (tree value_type, tree arg_types) return t; } +/* Build variant of function type ORIG_TYPE skipping ARGS_TO_SKIP. */ + +tree +build_function_type_skip_args (tree orig_type, bitmap args_to_skip) +{ + tree new_type = NULL; + tree args, new_args = NULL, t; + tree new_reversed; + int i = 0; + + for (args = TYPE_ARG_TYPES (orig_type); args && args != void_list_node; + args = TREE_CHAIN (args), i++) + if (!bitmap_bit_p (args_to_skip, i)) + new_args = tree_cons (NULL_TREE, TREE_VALUE (args), new_args); + + new_reversed = nreverse (new_args); + if (args) + { + if (new_reversed) + TREE_CHAIN (new_args) = void_list_node; + else + new_reversed = void_list_node; + } + gcc_assert (new_reversed); + + /* Use copy_node to preserve as much as possible from original type + (debug info, attribute lists etc.) + Exception is METHOD_TYPEs must have THIS argument. + When we are asked to remove it, we need to build new FUNCTION_TYPE + instead. */ + if (TREE_CODE (orig_type) != METHOD_TYPE + || !bitmap_bit_p (args_to_skip, 0)) + { + new_type = copy_node (orig_type); + TYPE_ARG_TYPES (new_type) = new_reversed; + } + else + new_type = build_function_type (TREE_TYPE (orig_type), new_reversed); + + /* This is a new type, not a copy of an old type. Need to reassociate + variants. We can handle everything except the main variant lazily. */ + t = TYPE_MAIN_VARIANT (orig_type); + if (orig_type != t) + { + TYPE_MAIN_VARIANT (new_type) = t; + TYPE_NEXT_VARIANT (new_type) = TYPE_NEXT_VARIANT (t); + TYPE_NEXT_VARIANT (t) = new_type; + } + else + { + TYPE_MAIN_VARIANT (new_type) = new_type; + TYPE_NEXT_VARIANT (new_type) = NULL; + } + return new_type; +} + +/* Build variant of function type ORIG_TYPE skipping ARGS_TO_SKIP. + + Arguments from DECL_ARGUMENTS list can't be removed now, since they are + linked by TREE_CHAIN directly. It is caller responsibility to eliminate + them when they are being duplicated (i.e. copy_arguments_for_versioning). */ + +tree +build_function_decl_skip_args (tree orig_decl, bitmap args_to_skip) +{ + tree new_decl = copy_node (orig_decl); + tree new_type; + + new_type = TREE_TYPE (orig_decl); + if (prototype_p (new_type)) + new_type = build_function_type_skip_args (new_type, args_to_skip); + TREE_TYPE (orig_decl) = new_type; + return new_decl; +} + /* Build a function type. The RETURN_TYPE is the type returned by the function. If VAARGS is set, no void_type_node is appended to the the list. ARGP muse be alway be terminated be a NULL_TREE. */ @@ -5893,9 +5968,9 @@ build_function_type_list_1 (bool vaargs, tree return_type, va_list argp) if (vaargs) { - last = args; - if (args != NULL_TREE) - args = nreverse (args); + last = args; + if (args != NULL_TREE) + args = nreverse (args); gcc_assert (args != NULL_TREE && last != void_list_node); } else if (args == NULL_TREE) @@ -3994,6 +3994,8 @@ extern tree build_index_2_type (tree, tree); extern tree build_array_type (tree, tree); extern tree build_function_type (tree, tree); extern tree build_function_type_list (tree, ...); +extern tree build_function_type_skip_args (tree, bitmap); +extern tree build_function_decl_skip_args (tree, bitmap); extern tree build_varargs_function_type_list (tree, ...); extern tree build_method_type_directly (tree, tree, tree); extern tree build_method_type (tree, tree); |