> > > The problem here is that LTRANS will run the standard pipeline > > > over a callgraph that hasn't been "settled" (i.e., no inlining > > > decisions have been applied yet). Perhaps the first thing LTRANS > > > should do is just call execute_all_ipa_transforms() and then > > > proceed with the regular pipeline. > > > > To make this extra clean, we can just create a execute_wpa_decisions > > pass that only runs in ltrans and is the first one. > > > > We will have a similar problem when we decide to implement ipa-cp in > > wpa. But we can solve this problem when we get there. > > I have ipa-cp using clones pretty much ready (it passes regtesting and I > am just bootstrapping and hoping to have patch out till end of week > finally) > > Honza Hi, and this is the patch to make ipa-cp proper IPA_PASS I plan to give it some extra testing and commit it to pretty-ipa. It basically allows any IPA_PASS to create a new clone of any function and attach info how to update it's arguments and what to replace for them. Clones are no longer flat structure expected to all have same declaratios for inliing, but form a tree so subsequent passes can clone the clones at their will. I expect that this will be enough for most of the clonning we will want to do at IPA stage, other stuff can be handled by IPA_PASS' own transform method.
There are still has few rough edges, especially it is not updating profile in clones CFG correctly and there are couple cleanups possible with this change, but it is not critical. I also plan to merge it with Martin's IPA-SRA, in particular his method of updating call sites, so the current cgraph_node->clone field will be replaced by more generic machinery. Finally we might need to be more cureful about when to materialize the clone that is currently done in one global pass: when pass A makes change to function and later pass B decides to clone it, we probably want to first apply pass A transform and then copy function body to pass B clone and apply pass B transform on both. This will need little extra care once we have multiple IPA passes performing clonning and doing some interesting transformations on them. (well, all so far planned stuff like IPA-SRA, dead argument removal, inlinig, clonning and ipa-cp will not excercise this problem) Sadly patch won't apply on LTO as it is. It needs earlier patches http://gcc.gnu.org/ml/gcc-patches/2008-11/msg00814.html http://gcc.gnu.org/ml/gcc-patches/2008-11/msg00831.html Honza * cgraph.c: Do not include varray.h (cgraph_set_call_stmt_including_clones, cgraph_create_edge_including_clones): New functions. (cgraph_remove_node): Update clone tree correctly. (cgraph_clone_node): Update clone datastructure. (cgraph_create_virtual_clone): New functio. * cgraph.h (struct ipa_replace_map): Move here from ipa-prop.h (ipa_replace_map_p): New type and vector. (cgraph_set_call_stmt_including_clones, cgraph_create_edge_including_clones, cgraph_create_virtual_clone): Declare. (cgraph_function_versioning): Use vector instead of varray. (tree_function_versioning): Declare here. (cgraph_materialize_all_clones): New function. * ipa-cp (ipcp_update_cloned_node): Remove (ipcp_create_replace_map): Allocate replace_map in GGC. (ipcp_update_callgraph): Only create virtual nodes. (ipcp_update_bb_counts, ipcp_update_edges_counts): Remove. (ipcp_update_profiling): Only update cgraph. (ipcp_insert_stage): Use GGC for skip_args bitmap; use vector for replace_map. * cgraphunit.c (clone_of_p): New function. (verify_cgraph_node): Handle cones correctly. (cgraph_optimize): Do cgraph_materialize_all_clones. (cgraph_materialize_clone, cgraph_materialize_all_clones): Update. * ipa-inline.c (cgraph_default_inline_p): Use analyzed flag instead of looking into the body. * ipa.c: Include gimple.h (cgraph_remove_unreachable_nodes): Functions that need to be instantiated to clones are needed. * ipa-prop.h (ipa_replace_map): Move to cgraph.h * tree-inline.c: Do not include varray.h (copy_bb): Properly update edges; take care of creating new edges for new call statements. (expand_call_inline): Edge should always exist. (tree_function_versioning): Use vector. * tree-inline.h: Remove varray.h (tree_function_versioning): Remove. * passes.c (do_per_function): Only do functions with bodies. Index: cgraph.c =================================================================== *** cgraph.c (revision 144122) --- cgraph.c (working copy) *************** The callgraph: *** 78,84 **** #include "target.h" #include "basic-block.h" #include "cgraph.h" - #include "varray.h" #include "output.h" #include "intl.h" #include "gimple.h" --- 78,83 ---- *************** cgraph_set_call_stmt (struct cgraph_edge *** 652,657 **** --- 651,727 ---- } } + /* Like cgraph_set_call_stmt but walk the clone tree and update all clones sharing + same function body. */ + + void + cgraph_set_call_stmt_including_clones (struct cgraph_node *orig, + gimple old_stmt, gimple new_stmt) + { + struct cgraph_node *node; + struct cgraph_edge *edge = cgraph_edge (orig, old_stmt); + + if (edge) + cgraph_set_call_stmt (edge, new_stmt); + if (orig->clones) + for (node = orig->clones; node != orig;) + { + struct cgraph_edge *edge = cgraph_edge (node, old_stmt); + + if (edge) + cgraph_set_call_stmt (edge, new_stmt); + if (node->clones) + node = node->clones; + else if (node->next_sibling_clone) + node = node->next_sibling_clone; + else + { + while (node != orig && !node->next_sibling_clone) + node = node->clone_of; + if (node != orig) + node = node->next_sibling_clone; + } + } + } + + /* Like cgraph_create_edge walk the clone tree and update all clones sharing + same function body. + + TODO: COUNT and LOOP_DEPTH should be properly distributed based on relative + frequencies of the clones. + */ + + void + cgraph_create_edge_including_clones (struct cgraph_node *orig, struct cgraph_node *callee, + gimple stmt, gcov_type count, int freq, + int loop_depth, + cgraph_inline_failed_t reason) + { + struct cgraph_node *node; + + cgraph_create_edge (orig, callee, stmt, count, freq, loop_depth)->inline_failed = + reason; + + if (orig->clones) + for (node = orig->clones; node != orig;) + { + cgraph_create_edge (node, callee, stmt, count, freq, + loop_depth)->inline_failed = reason; + + if (node->clones) + node = node->clones; + else if (node->next_sibling_clone) + node = node->next_sibling_clone; + else + { + while (node != orig && !node->next_sibling_clone) + node = node->clone_of; + if (node != orig) + node = node->next_sibling_clone; + } + } + } + /* Create edge from CALLER to CALLEE in the cgraph. */ struct cgraph_edge * *************** cgraph_remove_node (struct cgraph_node * *** 978,1035 **** slot = htab_find_slot (cgraph_hash, node, NO_INSERT); if (*slot == node) { ! gcc_assert (!node->clone_of); ! gcc_assert (!node->next_sibling_clone); ! if (node->clones) { - struct cgraph_node *new_node = node->clones; struct cgraph_node *n; ! *slot = new_node; ! if (new_node->next_sibling_clone) { ! n = new_node->next_sibling_clone; ! while (n->next_sibling_clone) { ! n->clone_of = new_node; ! n = n->next_sibling_clone; } - n->clone_of = new_node; - if (new_node->clones) - new_node->clones->prev_sibling_clone = n; - n->next_sibling_clone = new_node->clones; - new_node->clones = new_node->next_sibling_clone; - new_node->next_sibling_clone->prev_sibling_clone = NULL; - new_node->next_sibling_clone = NULL; } ! new_node->clone_of = NULL; } else { htab_clear_slot (cgraph_hash, slot); kill_body = true; } } else ! { ! gcc_assert (node->clone_of); ! if (node->prev_sibling_clone) ! node->prev_sibling_clone->next_sibling_clone = node->next_sibling_clone; ! else if (node->clone_of) ! node->clone_of->clones = node->next_sibling_clone; ! if (node->next_sibling_clone) ! node->next_sibling_clone->prev_sibling_clone = node->prev_sibling_clone; ! if (node->clones) ! { ! struct cgraph_node *n; ! for (n = node->clones; n->next_sibling_clone; n = n->next_sibling_clone) ! n->clone_of = node->clone_of; ! n->clone_of = node->clone_of; ! n->next_sibling_clone = node->clone_of->clones; ! if (node->clone_of->clones) ! node->clone_of->clones->prev_sibling_clone = n; ! node->clone_of->clones = node->clones; ! } } /* While all the clones are removed after being proceeded, the function --- 1048,1147 ---- slot = htab_find_slot (cgraph_hash, node, NO_INSERT); if (*slot == node) { ! struct cgraph_node *next_inline_clone; ! ! for (next_inline_clone = node->clones; ! next_inline_clone && next_inline_clone->decl != node->decl; ! next_inline_clone = next_inline_clone->next_sibling_clone) ! ; ! ! /* If there is inline clone of the node being removed, we need ! to put it into the position of removed node and reorganize all ! other clones to be based on it. */ ! if (next_inline_clone) { struct cgraph_node *n; + struct cgraph_node *new_clones; + + *slot = next_inline_clone; + + /* Unlink inline clone from the list of clones of removed node. */ + if (next_inline_clone->next_sibling_clone) + next_inline_clone->next_sibling_clone->prev_sibling_clone + = next_inline_clone->prev_sibling_clone; + if (next_inline_clone->prev_sibling_clone) + { + next_inline_clone->prev_sibling_clone->next_sibling_clone + = next_inline_clone->next_sibling_clone; + } + else + node->clones = next_inline_clone->next_sibling_clone; + + new_clones = node->clones; + node->clones = NULL; + + /* Copy clone info. */ + next_inline_clone->clone = node->clone; + + /* Now place it into clone tree at same level at NODE. */ + next_inline_clone->clone_of = node->clone_of; + next_inline_clone->prev_sibling_clone = NULL; + next_inline_clone->next_sibling_clone = NULL; + if (node->clone_of) + { + next_inline_clone->next_sibling_clone = node->clone_of->clones; + node->clone_of->clones = next_inline_clone; + } ! /* Merge the clone list. */ ! if (new_clones) { ! if (!next_inline_clone->clones) ! next_inline_clone->clones = new_clones; ! else { ! n = next_inline_clone->clones; ! while (n->next_sibling_clone) ! n = n->next_sibling_clone; ! n->next_sibling_clone = new_clones; ! new_clones->prev_sibling_clone = n; } } ! ! /* Update clone_of pointers. */ ! n = new_clones; ! while (n) ! { ! n->clone_of = next_inline_clone; ! n = n->next_sibling_clone; ! } } else { htab_clear_slot (cgraph_hash, slot); kill_body = true; } + } else ! gcc_assert (node->clone_of); ! if (node->prev_sibling_clone) ! node->prev_sibling_clone->next_sibling_clone = node->next_sibling_clone; ! else if (node->clone_of) ! node->clone_of->clones = node->next_sibling_clone; ! if (node->next_sibling_clone) ! node->next_sibling_clone->prev_sibling_clone = node->prev_sibling_clone; ! if (node->clones) ! { ! struct cgraph_node *n; ! ! for (n = node->clones; n->next_sibling_clone; n = n->next_sibling_clone) ! n->clone_of = node->clone_of; ! n->clone_of = node->clone_of; ! n->next_sibling_clone = node->clone_of->clones; ! if (node->clone_of->clones) ! node->clone_of->clones->prev_sibling_clone = n; ! node->clone_of->clones = node->clones; } /* While all the clones are removed after being proceeded, the function *************** dump_cgraph_node (FILE *f, struct cgraph *** 1179,1184 **** --- 1291,1300 ---- fprintf (f, " (inline copy in %s/%i)", cgraph_node_name (node->global.inlined_to), node->global.inlined_to->uid); + if (node->clone_of) + fprintf (f, " (clone of %s/%i)", + cgraph_node_name (node->clone_of), + node->clone_of->uid); if (cgraph_function_flags_ready) fprintf (f, " availability:%s", cgraph_availability_names [cgraph_function_body_availability (node)]); *************** cgraph_clone_node (struct cgraph_node *n *** 1398,1403 **** --- 1514,1520 ---- new_node->global = n->global; new_node->rtl = n->rtl; new_node->count = count; + new_node->clone = n->clone; if (n->count) { if (new_node->count > n->count) *************** cgraph_clone_node (struct cgraph_node *n *** 1428,1433 **** --- 1545,1627 ---- return new_node; } + /* Create callgraph node clone with new declaration. The actual body will + be copied later at compilation stage. + + TODO: after merging in ipa-sra use function call notes instead of args_to_skip + bitmap interface. + */ + struct cgraph_node * + cgraph_create_virtual_clone (struct cgraph_node *old_node, + VEC(cgraph_edge_p,heap) *redirect_callers, + VEC(ipa_replace_map_p,gc) *tree_map, + bitmap args_to_skip) + { + tree old_decl = old_node->decl; + struct cgraph_node *new_node = NULL; + tree new_decl; + struct cgraph_node key, **slot; + unsigned i; + struct cgraph_edge *e; + + gcc_assert (tree_versionable_function_p (old_decl)); + + /* Make a new FUNCTION_DECL tree node */ + if (!args_to_skip) + new_decl = copy_node (old_decl); + else + new_decl = build_function_decl_skip_args (old_decl, args_to_skip); + DECL_STRUCT_FUNCTION (new_decl) = NULL; + + /* Generate a new name for the new version. */ + DECL_NAME (new_decl) = create_tmp_var_name (NULL); + SET_DECL_ASSEMBLER_NAME (new_decl, DECL_NAME (new_decl)); + SET_DECL_RTL (new_decl, NULL); + + new_node = cgraph_clone_node (old_node, 0, 0, 0, false); + new_node->decl = new_decl; + /* Update the properties. + Make clone visible only within this translation unit. Make sure + that is not weak also. + ??? We cannot use COMDAT linkage because there is no + ABI support for this. */ + DECL_EXTERNAL (new_node->decl) = 0; + DECL_ONE_ONLY (new_node->decl) = 0; + TREE_PUBLIC (new_node->decl) = 0; + DECL_COMDAT (new_node->decl) = 0; + DECL_WEAK (new_node->decl) = 0; + new_node->clone.tree_map = tree_map; + new_node->clone.args_to_skip = args_to_skip; + new_node->local.externally_visible = 0; + new_node->local.local = 1; + new_node->lowered = true; + new_node->reachable = true; + + key.decl = new_decl; + slot = (struct cgraph_node **) htab_find_slot (cgraph_hash, &key, INSERT); + gcc_assert (!*slot); + *slot = new_node; + if (assembler_name_hash) + { + void **aslot; + tree name = DECL_ASSEMBLER_NAME (new_decl); + + aslot = htab_find_slot_with_hash (assembler_name_hash, name, + decl_assembler_name_hash (name), + INSERT); + gcc_assert (!*aslot); + *aslot = new_node; + } + for (i = 0; VEC_iterate (cgraph_edge_p, redirect_callers, i, e); i++) + { + /* Redirect calls to the old version node to point to its new + version. */ + cgraph_redirect_edge_callee (e, new_node); + } + + return new_node; + } + /* NODE is no longer nested function; update cgraph accordingly. */ void cgraph_unnest_node (struct cgraph_node *node) Index: cgraph.h =================================================================== *** cgraph.h (revision 144122) --- cgraph.h (working copy) *************** struct cgraph_rtl_info GTY(()) *** 128,133 **** --- 128,156 ---- unsigned int preferred_incoming_stack_boundary; }; + /* Represent which DECL tree (or reference to such tree) + will be replaced by another tree while versioning. */ + struct ipa_replace_map GTY(()) + { + /* The tree that will be replaced. */ + tree old_tree; + /* The new (replacing) tree. */ + tree new_tree; + /* True when a substitution should be done, false otherwise. */ + bool replace_p; + /* True when we replace a reference to old_tree. */ + bool ref_p; + }; + typedef struct ipa_replace_map *ipa_replace_map_p; + DEF_VEC_P(ipa_replace_map_p); + DEF_VEC_ALLOC_P(ipa_replace_map_p,gc); + + struct cgraph_clone_info GTY(()) + { + VEC(ipa_replace_map_p,gc)* tree_map; + bitmap args_to_skip; + }; + /* The cgraph data structure. Each function decl has assigned cgraph_node listing callees and callers. */ *************** struct cgraph_node GTY((chain_next ("%h. *** 160,165 **** --- 183,189 ---- struct cgraph_local_info local; struct cgraph_global_info global; struct cgraph_rtl_info rtl; + struct cgraph_clone_info clone; /* Expected number of executions: calculated in profile.c. */ gcov_type count; *************** struct cgraph_node *cgraph_node (tree); *** 327,332 **** --- 351,361 ---- struct cgraph_node *cgraph_node_for_asm (tree asmname); struct cgraph_edge *cgraph_edge (struct cgraph_node *, gimple); void cgraph_set_call_stmt (struct cgraph_edge *, gimple); + void cgraph_set_call_stmt_including_clones (struct cgraph_node *, gimple, gimple); + void cgraph_create_edge_including_clones (struct cgraph_node *, + struct cgraph_node *, + gimple, gcov_type, int, int, + cgraph_inline_failed_t); void cgraph_update_edges_for_call_stmt (gimple, gimple); struct cgraph_local_info *cgraph_local_info (tree); struct cgraph_global_info *cgraph_global_info (tree); *************** void cgraph_unnest_node (struct cgraph_n *** 348,353 **** --- 377,386 ---- enum availability cgraph_function_body_availability (struct cgraph_node *); void cgraph_add_new_function (tree, bool); const char* cgraph_inline_failed_string (cgraph_inline_failed_t); + struct cgraph_node * cgraph_create_virtual_clone (struct cgraph_node *old_node, + VEC(cgraph_edge_p,heap)*, + VEC(ipa_replace_map_p,gc)* tree_map, + bitmap args_to_skip); /* In cgraphunit.c */ void cgraph_finalize_function (tree, bool); *************** void cgraph_reset_static_var_maps (void) *** 365,372 **** void init_cgraph (void); struct cgraph_node *cgraph_function_versioning (struct cgraph_node *, VEC(cgraph_edge_p,heap)*, ! 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); --- 398,406 ---- void init_cgraph (void); struct cgraph_node *cgraph_function_versioning (struct cgraph_node *, VEC(cgraph_edge_p,heap)*, ! VEC(ipa_replace_map_p,gc)*, bitmap); + void tree_function_versioning (tree, tree, VEC (ipa_replace_map_p,gc)*, bool, bitmap); void cgraph_analyze_function (struct cgraph_node *); struct cgraph_node *save_inline_function_body (struct cgraph_node *); void record_references_in_initializer (tree); *************** struct cgraph_2edge_hook_list *cgraph_ad *** 395,400 **** --- 429,435 ---- void cgraph_remove_edge_duplication_hook (struct cgraph_2edge_hook_list *); struct cgraph_2node_hook_list *cgraph_add_node_duplication_hook (cgraph_2node_hook, void *); void cgraph_remove_node_duplication_hook (struct cgraph_2node_hook_list *); + void cgraph_materialize_all_clones (void); /* In cgraphbuild.c */ unsigned int rebuild_cgraph_edges (void); Index: ipa-cp.c =================================================================== *** ipa-cp.c (revision 144122) --- ipa-cp.c (working copy) *************** ipcp_analyze_node (struct cgraph_node *n *** 186,219 **** ipa_detect_param_modifications (node); } - /* Recompute all local information since node might've got new - direct calls after cloning. */ - static void - ipcp_update_cloned_node (struct cgraph_node *new_node) - { - /* We might've introduced new direct calls. */ - push_cfun (DECL_STRUCT_FUNCTION (new_node->decl)); - current_function_decl = new_node->decl; - rebuild_cgraph_edges (); - - /* Indirect inlinng rely on fact that we've already analyzed - the body.. */ - if (flag_indirect_inlining) - { - struct cgraph_edge *cs; - - ipcp_analyze_node (new_node); - - for (cs = new_node->callees; cs; cs = cs->next_callee) - { - ipa_count_arguments (cs); - ipa_compute_jump_functions (cs); - } - } - pop_cfun (); - current_function_decl = NULL; - } - /* Return scale for NODE. */ static inline gcov_type ipcp_get_node_scale (struct cgraph_node *node) --- 186,191 ---- *************** ipcp_create_replace_map (tree parm_tree, *** 872,878 **** struct ipa_replace_map *replace_map; tree const_val; ! replace_map = XCNEW (struct ipa_replace_map); const_val = build_const_val (lat, TREE_TYPE (parm_tree)); if (dump_file) { --- 844,850 ---- struct ipa_replace_map *replace_map; tree const_val; ! replace_map = GGC_NEW (struct ipa_replace_map); const_val = build_const_val (lat, TREE_TYPE (parm_tree)); if (dump_file) { *************** ipcp_update_callgraph (void) *** 959,981 **** 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 = gimple_call_copy_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); --- 931,937 ---- for (cs = node->callers; cs; cs = next) { next = cs->next_caller; ! if (!ipcp_node_is_clone (cs->caller) && ipcp_need_redirect_p (cs)) { cgraph_redirect_edge_callee (cs, orig_node); gimple_call_set_fndecl (cs->call_stmt, orig_node->decl); *************** ipcp_update_callgraph (void) *** 984,1012 **** } } - /* Update all cfg basic blocks in NODE according to SCALE. */ - static void - ipcp_update_bb_counts (struct cgraph_node *node, gcov_type scale) - { - basic_block bb; - - FOR_ALL_BB_FN (bb, DECL_STRUCT_FUNCTION (node->decl)) - bb->count = bb->count * scale / REG_BR_PROB_BASE; - } - - /* Update all cfg edges in NODE according to SCALE. */ - static void - ipcp_update_edges_counts (struct cgraph_node *node, gcov_type scale) - { - basic_block bb; - edge_iterator ei; - edge e; - - FOR_ALL_BB_FN (bb, DECL_STRUCT_FUNCTION (node->decl)) - FOR_EACH_EDGE (e, ei, bb->succs) - e->count = e->count * scale / REG_BR_PROB_BASE; - } - /* Update profiling info for versioned functions and the functions they were versioned from. */ static void --- 940,945 ---- *************** ipcp_update_profiling (void) *** 1030,1039 **** cs->count = cs->count * scale / REG_BR_PROB_BASE; for (cs = orig_node->callees; cs; cs = cs->next_callee) cs->count = cs->count * scale_complement / REG_BR_PROB_BASE; - ipcp_update_bb_counts (node, scale); - ipcp_update_bb_counts (orig_node, scale_complement); - ipcp_update_edges_counts (node, scale); - ipcp_update_edges_counts (orig_node, scale_complement); } } } --- 963,968 ---- *************** ipcp_insert_stage (void) *** 1158,1164 **** struct cgraph_node *node, *node1 = NULL; int i; VEC (cgraph_edge_p, heap) * redirect_callers; ! varray_type replace_trees; int node_callers, count; tree parm_tree; struct ipa_replace_map *replace_param; --- 1087,1093 ---- struct cgraph_node *node, *node1 = NULL; int i; VEC (cgraph_edge_p, heap) * redirect_callers; ! VEC (ipa_replace_map_p,gc)* replace_trees; int node_callers, count; tree parm_tree; struct ipa_replace_map *replace_param; *************** ipcp_insert_stage (void) *** 1240,1248 **** info = IPA_NODE_REF (node); count = ipa_get_param_count (info); ! 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_lattice (info, i); --- 1169,1176 ---- info = IPA_NODE_REF (node); count = ipa_get_param_count (info); ! replace_trees = VEC_alloc (ipa_replace_map_p, gc, 1); ! args_to_skip = BITMAP_GGC_ALLOC (); for (i = 0; i < count; i++) { struct ipcp_lattice *lat = ipcp_get_lattice (info, i); *************** ipcp_insert_stage (void) *** 1261,1267 **** { replace_param = ipcp_create_replace_map (parm_tree, lat); ! VARRAY_PUSH_GENERIC_PTR (replace_trees, replace_param); bitmap_set_bit (args_to_skip, i); } } --- 1189,1195 ---- { replace_param = ipcp_create_replace_map (parm_tree, lat); ! VEC_safe_push (ipa_replace_map_p, gc, replace_trees, replace_param); bitmap_set_bit (args_to_skip, i); } } *************** ipcp_insert_stage (void) *** 1277,1287 **** /* Redirecting all the callers of the node to the new versioned node. */ node1 = ! 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) continue; if (dump_file) --- 1205,1216 ---- /* Redirecting all the callers of the node to the new versioned node. */ node1 = ! cgraph_create_virtual_clone (node, redirect_callers, replace_trees, ! args_to_skip); ! args_to_skip = NULL; VEC_free (cgraph_edge_p, heap, redirect_callers); ! replace_trees = NULL; ! if (node1 == NULL) continue; if (dump_file) *************** ipcp_insert_stage (void) *** 1289,1296 **** cgraph_node_name (node), (int)growth, (int)new_size); ipcp_init_cloned_node (node, node1); ! /* We've possibly introduced direct calls. */ ! ipcp_update_cloned_node (node1); if (dump_file) dump_function_to_file (node1->decl, dump_file, dump_flags); --- 1218,1224 ---- cgraph_node_name (node), (int)growth, (int)new_size); ipcp_init_cloned_node (node, node1); ! /* TODO: We can use indirect inlning info to produce new calls. */ if (dump_file) dump_function_to_file (node1->decl, dump_file, dump_flags); Index: cgraphunit.c =================================================================== *** cgraphunit.c (revision 144122) --- cgraphunit.c (working copy) *************** cgraph_mark_if_needed (tree decl) *** 548,553 **** --- 548,562 ---- cgraph_mark_needed_node (node); } + /* Return TRUE if NODE2 is equivalent to NODE or its clone. */ + static bool + clone_of_p (struct cgraph_node *node, struct cgraph_node *node2) + { + while (node != node2 && node2) + node2 = node2->clone_of; + return node2 != NULL; + } + /* Verify cgraph nodes of given cgraph node. */ void verify_cgraph_node (struct cgraph_node *node) *************** verify_cgraph_node (struct cgraph_node * *** 673,679 **** error_found = true; } ! if (node->analyzed && !TREE_ASM_WRITTEN (node->decl) && (!DECL_EXTERNAL (node->decl) || node->global.inlined_to)) { --- 682,688 ---- error_found = true; } ! if (node->analyzed && gimple_has_body_p (node->decl) && !TREE_ASM_WRITTEN (node->decl) && (!DECL_EXTERNAL (node->decl) || node->global.inlined_to)) { *************** verify_cgraph_node (struct cgraph_node * *** 702,709 **** debug_gimple_stmt (stmt); error_found = true; } ! if (e->callee->decl != cgraph_node (decl)->decl ! && e->inline_failed) { error ("edge points to wrong declaration:"); debug_tree (e->callee->decl); --- 711,718 ---- debug_gimple_stmt (stmt); error_found = true; } ! if (!clone_of_p (cgraph_node (decl), e->callee) ! && !e->callee->global.inlined_to) { error ("edge points to wrong declaration:"); debug_tree (e->callee->decl); *************** cgraph_optimize (void) *** 1334,1339 **** --- 1343,1349 ---- verify_cgraph (); #endif + cgraph_materialize_all_clones (); cgraph_mark_functions_to_output (); cgraph_state = CGRAPH_STATE_EXPANSION; *************** cgraph_copy_node_for_versioning (struct *** 1550,1556 **** struct cgraph_node * cgraph_function_versioning (struct cgraph_node *old_version_node, VEC(cgraph_edge_p,heap) *redirect_callers, ! varray_type tree_map, bitmap args_to_skip) { tree old_decl = old_version_node->decl; --- 1560,1566 ---- struct cgraph_node * cgraph_function_versioning (struct cgraph_node *old_version_node, VEC(cgraph_edge_p,heap) *redirect_callers, ! VEC (ipa_replace_map_p,gc)* tree_map, bitmap args_to_skip) { tree old_decl = old_version_node->decl; *************** save_inline_function_body (struct cgraph *** 1659,1662 **** --- 1669,1792 ---- return first_clone; } + /* Given virtual clone, turn it into actual clone. */ + static void + cgraph_materialize_clone (struct cgraph_node *node) + { + /* Copy the OLD_VERSION_NODE function tree to the new version. */ + tree_function_versioning (node->clone_of->decl, node->decl, + node->clone.tree_map, true, + node->clone.args_to_skip); + + /* Function is no longer clone. */ + if (node->next_sibling_clone) + node->next_sibling_clone->prev_sibling_clone = node->prev_sibling_clone; + if (node->prev_sibling_clone) + node->prev_sibling_clone->next_sibling_clone = node->next_sibling_clone; + else + node->clone_of->clones = node->next_sibling_clone; + node->next_sibling_clone = NULL; + node->prev_sibling_clone = NULL; + node->clone_of = NULL; + } + + /* Once all functions from compilation unit are in memory, produce all clones + and update all calls. + We might also do this on demand if we don't want to bring all functions to + memory prior compilation, but current WHOPR implementation does that and it is + is bit easier to keep everything right in this order. */ + void + cgraph_materialize_all_clones (void) + { + struct cgraph_node *node; + bool stabilized = false; + + if (cgraph_dump_file) + fprintf (cgraph_dump_file, "Materializing clones\n"); + #ifdef ENABLE_CHECKING + verify_cgraph (); + #endif + + /* We can also do topological order, but number of iterations should be + bounded by number of IPA passes since single IPA pass is probably not + going to create clones of clones it created itself. */ + while (!stabilized) + { + stabilized = true; + for (node = cgraph_nodes; node; node = node->next) + { + if (node->clone_of && node->decl != node->clone_of->decl + && !gimple_has_body_p (node->decl)) + { + if (gimple_has_body_p (node->clone_of->decl)) + { + if (cgraph_dump_file) + fprintf (cgraph_dump_file, " clonning %s to %s", + cgraph_node_name (node->clone_of), + cgraph_node_name (node)); + cgraph_materialize_clone (node); + } + else + stabilized = false; + } + } + } + if (cgraph_dump_file) + fprintf (cgraph_dump_file, "Updating call sites\n"); + for (node = cgraph_nodes; node; node = node->next) + if (node->analyzed && !node->clone_of && gimple_has_body_p (node->decl)) + { + struct cgraph_edge *e; + + current_function_decl = node->decl; + push_cfun (DECL_STRUCT_FUNCTION (node->decl)); + for (e = node->callees; e; e = e->next_callee) + { + tree decl = gimple_call_fndecl (e->call_stmt); + if (decl != e->callee->decl) + { + gimple new_stmt; + gimple_stmt_iterator gsi; + + if (cgraph_dump_file) + { + fprintf (cgraph_dump_file, "updating call of %s in %s:", + cgraph_node_name (node), + cgraph_node_name (e->callee)); + print_gimple_stmt (cgraph_dump_file, e->call_stmt, 0, dump_flags); + } + + if (e->callee->clone.args_to_skip) + new_stmt = gimple_call_copy_skip_args (e->call_stmt, + e->callee->clone.args_to_skip); + else + new_stmt = e->call_stmt; + gimple_call_set_fndecl (new_stmt, e->callee->decl); + + gsi = gsi_for_stmt (e->call_stmt); + gsi_replace (&gsi, new_stmt, true); + + /* Update EH information too, just in case. */ + if (!stmt_could_throw_p (new_stmt) + && lookup_stmt_eh_region (new_stmt)) + remove_stmt_from_eh_region (new_stmt); + + cgraph_set_call_stmt_including_clones (node, e->call_stmt, new_stmt); + + if (cgraph_dump_file) + { + fprintf (cgraph_dump_file, " updated to:"); + print_gimple_stmt (cgraph_dump_file, e->call_stmt, 0, dump_flags); + } + } + } + pop_cfun (); + current_function_decl = NULL; + #ifdef ENABLE_CHECKING + verify_cgraph_node (node); + #endif + } + cgraph_remove_unreachable_nodes (false, cgraph_dump_file); + } + #include "gt-cgraphunit.h" Index: ipa-inline.c =================================================================== *** ipa-inline.c (revision 144122) --- ipa-inline.c (working copy) *************** cgraph_default_inline_p (struct cgraph_n *** 448,454 **** return false; } ! if (!DECL_STRUCT_FUNCTION (decl)->cfg) { if (reason) *reason = CIF_BODY_NOT_AVAILABLE; --- 448,454 ---- return false; } ! if (!n->analyzed) { if (reason) *reason = CIF_BODY_NOT_AVAILABLE; Index: ipa.c =================================================================== *** ipa.c (revision 144122) --- ipa.c (working copy) *************** along with GCC; see the file COPYING3. *** 25,30 **** --- 25,31 ---- #include "cgraph.h" #include "tree-pass.h" #include "timevar.h" + #include "gimple.h" /* Fill array order with all nodes with output flag set in the reverse topological order. */ *************** cgraph_remove_unreachable_nodes (bool be *** 142,147 **** --- 143,154 ---- e->callee->aux = first; first = e->callee; } + while (node->clone_of && !node->clone_of->aux && !gimple_has_body_p (node->decl)) + { + node = node->clone_of; + node->aux = first; + first = node; + } } /* Remove unreachable nodes. Extern inline functions need special care; Index: ipa-prop.h =================================================================== *** ipa-prop.h (revision 144122) --- ipa-prop.h (working copy) *************** struct ipcp_lattice *** 99,118 **** tree constant; }; - /* Represent which DECL tree (or reference to such tree) - will be replaced by another tree while versioning. */ - struct ipa_replace_map - { - /* The tree that will be replaced. */ - tree old_tree; - /* The new (replacing) tree. */ - tree new_tree; - /* True when a substitution should be done, false otherwise. */ - bool replace_p; - /* True when we replace a reference to old_tree. */ - bool ref_p; - }; - /* Each instance of the following structure describes a statement that calls a function parameter. Those referring to statements within the same function are linked in a list. */ --- 99,104 ---- Index: tree-inline.c =================================================================== *** tree-inline.c (revision 144122) --- tree-inline.c (working copy) *************** along with GCC; see the file COPYING3. *** 32,38 **** #include "params.h" #include "input.h" #include "insn-config.h" - #include "varray.h" #include "hashtab.h" #include "langhooks.h" #include "basic-block.h" --- 32,37 ---- *************** copy_bb (copy_body_data *id, basic_block *** 1291,1296 **** --- 1290,1297 ---- need to process all of them. */ do { + tree fn; + stmt = gsi_stmt (copy_gsi); if (is_gimple_call (stmt) && gimple_call_va_arg_pack_p (stmt) *************** copy_bb (copy_body_data *id, basic_block *** 1379,1427 **** callgraph edges and update or duplicate them. */ if (is_gimple_call (stmt)) { ! struct cgraph_node *node; ! struct cgraph_edge *edge; switch (id->transform_call_graph_edges) { case CB_CGE_DUPLICATE: ! edge = cgraph_edge (id->src_node, orig_stmt); ! if (edge) cgraph_clone_edge (edge, id->dst_node, stmt, REG_BR_PROB_BASE, 1, edge->frequency, true); break; case CB_CGE_MOVE_CLONES: ! if (id->dst_node->clones) ! for (node = id->dst_node->clones; node != id->dst_node;) ! { ! edge = cgraph_edge (node, orig_stmt); ! if (edge) ! cgraph_set_call_stmt (edge, stmt); ! if (node->clones) ! node = node->clones; ! else if (node->next_sibling_clone) ! node = node->next_sibling_clone; ! else ! { ! while (node != id->dst_node && !node->next_sibling_clone) ! node = node->clone_of; ! if (node != id->dst_node) ! node = node->next_sibling_clone; ! } ! } ! /* FALLTHRU */ case CB_CGE_MOVE: ! edge = cgraph_edge (id->dst_node, orig_stmt); ! if (edge) cgraph_set_call_stmt (edge, stmt); break; default: gcc_unreachable (); } } /* If you think we can abort here, you are wrong. --- 1380,1438 ---- callgraph edges and update or duplicate them. */ if (is_gimple_call (stmt)) { ! struct cgraph_edge *edge = cgraph_edge (id->src_node, orig_stmt); switch (id->transform_call_graph_edges) { case CB_CGE_DUPLICATE: ! if (edge) cgraph_clone_edge (edge, id->dst_node, stmt, REG_BR_PROB_BASE, 1, edge->frequency, true); break; case CB_CGE_MOVE_CLONES: ! cgraph_set_call_stmt_including_clones (id->dst_node, orig_stmt, stmt); ! break; case CB_CGE_MOVE: ! if (edge) cgraph_set_call_stmt (edge, stmt); break; default: gcc_unreachable (); } + + /* Constant propagation on argument done during inlining + may create new direct call. Produce an edge for it. */ + if (!edge && is_gimple_call (stmt) + && (fn = gimple_call_fndecl (stmt)) != NULL + && !cgraph_edge (id->dst_node, stmt)) + { + struct cgraph_node *dest = cgraph_node (fn); + + /* We have missing edge in the callgraph. This can happen in one case + where previous inlining turned indirect call into direct call by + constant propagating arguments. In all other cases we hit a bug + (incorrect node sharing is most common reason for missing edges. */ + gcc_assert (dest->needed || !dest->analyzed); + if (id->transform_call_graph_edges == CB_CGE_MOVE_CLONES) + cgraph_create_edge_including_clones (id->dst_node, dest, stmt, + bb->count, CGRAPH_FREQ_BASE, + bb->loop_depth, + CIF_ORIGINALLY_INDIRECT_CALL); + else + cgraph_create_edge (id->dst_node, dest, stmt, + bb->count, CGRAPH_FREQ_BASE, + bb->loop_depth)->inline_failed + = CIF_ORIGINALLY_INDIRECT_CALL; + if (dump_file) + { + fprintf (dump_file, "Created new direct edge to %s", + cgraph_node_name (dest)); + } + } } /* If you think we can abort here, you are wrong. *************** expand_call_inline (basic_block bb, gimp *** 3122,3150 **** cg_edge = cgraph_edge (id->dst_node, stmt); - /* Constant propagation on argument done during previous inlining - may create new direct call. Produce an edge for it. */ - if (!cg_edge) - { - struct cgraph_node *dest = cgraph_node (fn); - - /* We have missing edge in the callgraph. This can happen in one case - where previous inlining turned indirect call into direct call by - constant propagating arguments. In all other cases we hit a bug - (incorrect node sharing is most common reason for missing edges. */ - gcc_assert (dest->needed); - cgraph_create_edge (id->dst_node, dest, stmt, - bb->count, CGRAPH_FREQ_BASE, - bb->loop_depth)->inline_failed - = CIF_ORIGINALLY_INDIRECT_CALL; - if (dump_file) - { - fprintf (dump_file, "Created new direct edge to %s", - cgraph_node_name (dest)); - } - goto egress; - } - /* Don't try to inline functions that are not well-suited to inlining. */ if (!cgraph_inline_p (cg_edge, &reason)) --- 3133,3138 ---- *************** tree_versionable_function_p (tree fndecl *** 4189,4195 **** trees. If UPDATE_CLONES is set, the call_stmt fields 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, bitmap args_to_skip) { struct cgraph_node *old_version_node; --- 4177,4183 ---- trees. If UPDATE_CLONES is set, the call_stmt fields of edges of clones of the function will be updated. */ void ! tree_function_versioning (tree old_decl, tree new_decl, VEC(ipa_replace_map_p,gc)* tree_map, bool update_clones, bitmap args_to_skip) { struct cgraph_node *old_version_node; *************** tree_function_versioning (tree old_decl, *** 4221,4229 **** /* Generate a new name for the new version. */ if (!update_clones) { - DECL_NAME (new_decl) = create_tmp_var_name (NULL); - SET_DECL_ASSEMBLER_NAME (new_decl, DECL_NAME (new_decl)); - SET_DECL_RTL (new_decl, NULL_RTX); id.statements_to_fold = pointer_set_create (); } --- 4209,4214 ---- *************** tree_function_versioning (tree old_decl, *** 4268,4278 **** /* If there's a tree_map, prepare for substitution. */ if (tree_map) ! for (i = 0; i < VARRAY_ACTIVE_SIZE (tree_map); i++) { gimple init; ! replace_info ! = (struct ipa_replace_map *) VARRAY_GENERIC_PTR (tree_map, i); if (replace_info->replace_p) { tree op = replace_info->new_tree; --- 4253,4262 ---- /* If there's a tree_map, prepare for substitution. */ if (tree_map) ! for (i = 0; i < VEC_length (ipa_replace_map_p, tree_map); i++) { gimple init; ! replace_info = VEC_index (ipa_replace_map_p, tree_map, i); if (replace_info->replace_p) { tree op = replace_info->new_tree; Index: tree-inline.h =================================================================== *** tree-inline.h (revision 144122) --- tree-inline.h (working copy) *************** along with GCC; see the file COPYING3. *** 21,27 **** #ifndef GCC_TREE_INLINE_H #define GCC_TREE_INLINE_H - #include "varray.h" #include "pointer-set.h" --- 21,26 ---- *************** int estimate_num_insns (gimple, eni_weig *** 155,161 **** 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, bitmap); bool tree_can_inline_p (tree, tree); extern gimple_seq remap_gimple_seq (gimple_seq, copy_body_data *); --- 154,159 ---- Index: passes.c =================================================================== *** passes.c (revision 144122) --- passes.c (working copy) *************** do_per_function (void (*callback) (void *** 848,854 **** { struct cgraph_node *node; for (node = cgraph_nodes; node; node = node->next) ! if (node->analyzed) { push_cfun (DECL_STRUCT_FUNCTION (node->decl)); current_function_decl = node->decl; --- 848,854 ---- { struct cgraph_node *node; for (node = cgraph_nodes; node; node = node->next) ! if (node->analyzed && gimple_has_body_p (node->decl)) { push_cfun (DECL_STRUCT_FUNCTION (node->decl)); current_function_decl = node->decl;