Message ID | 20120509151515.GA1951@kam.mff.cuni.cz |
---|---|
State | New |
Headers | show |
Hi, after some thought, the changes into omp-low are not as obviously harmless as I originally tought. So i decided to handle this by separate patch. This patch simply makes cgraph to not release bodies of artificial functions that papers around the problem in easier way. Bootstrapped/regtested x86_64-linux, comitted. Honza * cgraph.h (cgraph_remove_unreachable_nodes): Rename to ... (symtab_remove_unreachable_nodes): ... this one. * ipa-cp.c (ipcp_driver): Do not remove unreachable nodes. * cgraphunit.c (ipa_passes): Update. * cgraphclones.c (cgraph_materialize_all_clones): Update. * cgraph.c (cgraph_release_function_body): Only turn initial into error mark when initial was previously set. * ipa-inline.c (ipa_inline): Update. * ipa.c: Include ipa-inline.h (enqueue_cgraph_node, enqueue_varpool_node): Remove. (enqueue_node): New function. (process_references): Update. (symtab_remove_unreachable_nodes): Cleanup. * passes.c (execute_todo, execute_one_pass): Update. Index: cgraph.c =================================================================== *** cgraph.c (revision 187335) --- cgraph.c (working copy) *************** cgraph_release_function_body (struct cgr *** 1162,1168 **** /* If the node is abstract and needed, then do not clear DECL_INITIAL of its associated function function declaration because it's needed to emit debug info later. */ ! if (!node->abstract_and_needed) DECL_INITIAL (node->symbol.decl) = error_mark_node; } --- 1162,1168 ---- /* If the node is abstract and needed, then do not clear DECL_INITIAL of its associated function function declaration because it's needed to emit debug info later. */ ! if (!node->abstract_and_needed && DECL_INITIAL (node->symbol.decl)) DECL_INITIAL (node->symbol.decl) = error_mark_node; } Index: cgraph.h =================================================================== *** cgraph.h (revision 187335) --- cgraph.h (working copy) *************** int compute_call_stmt_bb_frequency (tree *** 637,643 **** void record_references_in_initializer (tree, bool); /* In ipa.c */ ! bool cgraph_remove_unreachable_nodes (bool, FILE *); cgraph_node_set cgraph_node_set_new (void); cgraph_node_set_iterator cgraph_node_set_find (cgraph_node_set, struct cgraph_node *); --- 637,643 ---- void record_references_in_initializer (tree, bool); /* In ipa.c */ ! bool symtab_remove_unreachable_nodes (bool, FILE *); cgraph_node_set cgraph_node_set_new (void); cgraph_node_set_iterator cgraph_node_set_find (cgraph_node_set, struct cgraph_node *); Index: ipa-cp.c =================================================================== *** ipa-cp.c (revision 187335) --- ipa-cp.c (working copy) *************** ipcp_driver (void) *** 2445,2451 **** struct cgraph_2edge_hook_list *edge_duplication_hook_holder; struct topo_info topo; - cgraph_remove_unreachable_nodes (true,dump_file); ipa_check_create_node_params (); ipa_check_create_edge_args (); grow_next_edge_clone_vector (); --- 2445,2450 ---- Index: cgraphunit.c =================================================================== *** cgraphunit.c (revision 187335) --- cgraphunit.c (working copy) *************** ipa_passes (void) *** 1836,1842 **** because TODO is run before the subpasses. It is important to remove the unreachable functions to save works at IPA level and to get LTO symbol tables right. */ ! cgraph_remove_unreachable_nodes (true, cgraph_dump_file); /* If pass_all_early_optimizations was not scheduled, the state of the cgraph will not be properly updated. Update it now. */ --- 1836,1842 ---- because TODO is run before the subpasses. It is important to remove the unreachable functions to save works at IPA level and to get LTO symbol tables right. */ ! symtab_remove_unreachable_nodes (true, cgraph_dump_file); /* If pass_all_early_optimizations was not scheduled, the state of the cgraph will not be properly updated. Update it now. */ *************** compile (void) *** 1962,1968 **** /* This pass remove bodies of extern inline functions we never inlined. Do this later so other IPA passes see what is really going on. */ ! cgraph_remove_unreachable_nodes (false, dump_file); cgraph_global_info_ready = true; if (cgraph_dump_file) { --- 1962,1968 ---- /* This pass remove bodies of extern inline functions we never inlined. Do this later so other IPA passes see what is really going on. */ ! symtab_remove_unreachable_nodes (false, dump_file); cgraph_global_info_ready = true; if (cgraph_dump_file) { *************** compile (void) *** 1987,1993 **** cgraph_materialize_all_clones (); bitmap_obstack_initialize (NULL); execute_ipa_pass_list (all_late_ipa_passes); ! cgraph_remove_unreachable_nodes (true, dump_file); #ifdef ENABLE_CHECKING verify_symtab (); #endif --- 1987,1993 ---- cgraph_materialize_all_clones (); bitmap_obstack_initialize (NULL); execute_ipa_pass_list (all_late_ipa_passes); ! symtab_remove_unreachable_nodes (true, dump_file); #ifdef ENABLE_CHECKING verify_symtab (); #endif Index: cgraphclones.c =================================================================== *** cgraphclones.c (revision 187335) --- cgraphclones.c (working copy) *************** cgraph_materialize_all_clones (void) *** 870,876 **** #ifdef ENABLE_CHECKING verify_cgraph (); #endif ! cgraph_remove_unreachable_nodes (false, cgraph_dump_file); } #include "gt-cgraphclones.h" --- 870,876 ---- #ifdef ENABLE_CHECKING verify_cgraph (); #endif ! symtab_remove_unreachable_nodes (false, cgraph_dump_file); } #include "gt-cgraphclones.h" Index: ipa-inline.c =================================================================== *** ipa-inline.c (revision 187335) --- ipa-inline.c (working copy) *************** ipa_inline (void) *** 1717,1723 **** } inline_small_functions (); ! cgraph_remove_unreachable_nodes (true, dump_file); free (order); /* We already perform some inlining of functions called once during --- 1717,1723 ---- } inline_small_functions (); ! symtab_remove_unreachable_nodes (true, dump_file); free (order); /* We already perform some inlining of functions called once during Index: ipa.c =================================================================== *** ipa.c (revision 187335) --- ipa.c (working copy) *************** along with GCC; see the file COPYING3. *** 33,38 **** --- 33,39 ---- #include "tree-iterator.h" #include "ipa-utils.h" #include "pointer-set.h" + #include "ipa-inline.h" /* Look for all functions inlined to NODE and update their inlined_to pointers to INLINED_TO. */ *************** update_inlined_to_pointer (struct cgraph *** 49,55 **** } } ! /* Add cgraph NODE to queue starting at FIRST. The queue is linked via AUX pointers and terminated by pointer to 1. We enqueue nodes at two occasions: when we find them reachable or when we find --- 50,56 ---- } } ! /* Add symtab NODE to queue starting at FIRST. The queue is linked via AUX pointers and terminated by pointer to 1. We enqueue nodes at two occasions: when we find them reachable or when we find *************** update_inlined_to_pointer (struct cgraph *** 58,65 **** reachable. */ static void ! enqueue_cgraph_node (struct cgraph_node *node, struct cgraph_node **first, ! struct pointer_set_t *reachable) { /* Node is still in queue; do nothing. */ if (node->symbol.aux && node->symbol.aux != (void *) 2) --- 59,66 ---- reachable. */ static void ! enqueue_node (symtab_node node, symtab_node *first, ! struct pointer_set_t *reachable) { /* Node is still in queue; do nothing. */ if (node->symbol.aux && node->symbol.aux != (void *) 2) *************** enqueue_cgraph_node (struct cgraph_node *** 72,92 **** *first = node; } - /* Add varpool NODE to queue starting at FIRST. */ - - static void - enqueue_varpool_node (struct varpool_node *node, struct varpool_node **first) - { - node->symbol.aux = *first; - *first = node; - } - /* Process references. */ static void process_references (struct ipa_ref_list *list, ! struct cgraph_node **first, ! struct varpool_node **first_varpool, bool before_inlining_p, struct pointer_set_t *reachable) { --- 73,83 ---- *first = node; } /* Process references. */ static void process_references (struct ipa_ref_list *list, ! symtab_node *first, bool before_inlining_p, struct pointer_set_t *reachable) { *************** process_references (struct ipa_ref_list *** 97,114 **** if (symtab_function_p (ref->referred)) { struct cgraph_node *node = ipa_ref_node (ref); if (node->analyzed && (!DECL_EXTERNAL (node->symbol.decl) || node->alias || before_inlining_p)) pointer_set_insert (reachable, node); ! enqueue_cgraph_node (node, first, reachable); } else { struct varpool_node *node = ipa_ref_varpool_node (ref); ! if (!pointer_set_insert (reachable, node)) ! enqueue_varpool_node (node, first_varpool); } } } --- 88,108 ---- if (symtab_function_p (ref->referred)) { struct cgraph_node *node = ipa_ref_node (ref); + if (node->analyzed && (!DECL_EXTERNAL (node->symbol.decl) || node->alias || before_inlining_p)) pointer_set_insert (reachable, node); ! enqueue_node ((symtab_node) node, first, reachable); } else { struct varpool_node *node = ipa_ref_varpool_node (ref); ! ! if (node->analyzed) ! pointer_set_insert (reachable, node); ! enqueue_node ((symtab_node) node, first, reachable); } } } *************** has_addr_references_p (struct cgraph_nod *** 162,180 **** } /* Perform reachability analysis and reclaim all unreachable nodes. ! If BEFORE_INLINING_P is true this function is called before inlining ! decisions has been made. If BEFORE_INLINING_P is false this function also ! removes unneeded bodies of extern inline functions. */ bool ! cgraph_remove_unreachable_nodes (bool before_inlining_p, FILE *file) { ! struct cgraph_node *first = (struct cgraph_node *) (void *) 1; ! struct varpool_node *first_varpool = (struct varpool_node *) (void *) 1; struct cgraph_node *node, *next; struct varpool_node *vnode, *vnext; bool changed = false; struct pointer_set_t *reachable = pointer_set_create (); #ifdef ENABLE_CHECKING verify_symtab (); --- 156,218 ---- } /* Perform reachability analysis and reclaim all unreachable nodes. ! ! The algorithm is basically mark&sweep but with some extra refinements: ! ! - reachable extern inline functions needs special handling; the bodies needs ! to stay in memory until inlining in hope that they will be inlined. ! After inlining we release their bodies and turn them into unanalyzed ! nodes even when they are reachable. ! ! BEFORE_INLINING_P specify whether we are before or after inlining. ! ! - virtual functions are kept in callgraph even if they seem unreachable in ! hope calls to them will be devirtualized. ! ! Again we remove them after inlining. In late optimization some ! devirtualization may happen, but it is not importnat since we won't inline ! the call. In theory early opts and IPA should work out all important cases. ! ! - virtual clones needs bodies of their origins for later materialization; ! this means that we want to keep the body even if the origin is unreachable ! otherwise. To avoid origin from sitting in the callgraph and being ! walked by IPA passes, we turn them into unanalyzed nodes with body ! defined. ! ! We maintain set of function declaration where body needs to stay in ! body_needed_for_clonning ! ! Inline clones represent special case: their declaration match the ! declaration of origin and cgraph_remove_node already knows how to ! reshape callgraph and preserve body when offline copy of function or ! inline clone is being removed. ! ! We maintain queue of both reachable symbols (i.e. defined symbols that needs ! to stay) and symbols that are in boundary (i.e. external symbols referenced ! by reachable symbols or origins of clones). The queue is represented ! as linked list by AUX pointer terminated by 1. ! ! A the end we keep all reachable symbols. For symbols in boundary we always ! turn definition into a declaration, but we may keep function body around ! based on body_needed_for_clonning ! ! All symbols that enter the queue have AUX pointer non-zero and are in the ! boundary. Pointer set REACHABLE is used to track reachable symbols. ! ! Every symbol can be visited twice - once as part of boundary and once ! as real reachable symbol. enqueue_node needs to decide whether the ! node needs to be re-queued for second processing. For this purpose ! we set AUX pointer of processed symbols in the boundary to constant 2. */ bool ! symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file) { ! symtab_node first = (symtab_node) (void *) 1; struct cgraph_node *node, *next; struct varpool_node *vnode, *vnext; bool changed = false; struct pointer_set_t *reachable = pointer_set_create (); + struct pointer_set_t *body_needed_for_clonning = pointer_set_create (); #ifdef ENABLE_CHECKING verify_symtab (); *************** cgraph_remove_unreachable_nodes (bool be *** 191,198 **** This is mostly when they can be referenced externally. Inline clones are special since their declarations are shared with master clone and thus cgraph_can_remove_if_no_direct_calls_and_refs_p should not be called on them. */ ! FOR_EACH_FUNCTION (node) ! if (node->analyzed && !node->global.inlined_to && (!cgraph_can_remove_if_no_direct_calls_and_refs_p (node) /* Keep around virtual functions for possible devirtualization. */ || (before_inlining_p --- 229,236 ---- This is mostly when they can be referenced externally. Inline clones are special since their declarations are shared with master clone and thus cgraph_can_remove_if_no_direct_calls_and_refs_p should not be called on them. */ ! FOR_EACH_DEFINED_FUNCTION (node) ! if (!node->global.inlined_to && (!cgraph_can_remove_if_no_direct_calls_and_refs_p (node) /* Keep around virtual functions for possible devirtualization. */ || (before_inlining_p *************** cgraph_remove_unreachable_nodes (bool be *** 200,397 **** && (DECL_COMDAT (node->symbol.decl) || DECL_EXTERNAL (node->symbol.decl))))) { gcc_assert (!node->global.inlined_to); - enqueue_cgraph_node (node, &first, reachable); pointer_set_insert (reachable, node); } else gcc_assert (!node->symbol.aux); /* Mark variables that are obviously needed. */ ! FOR_EACH_VARIABLE (vnode) { ! if ((vnode->analyzed || vnode->symbol.force_output) ! && !varpool_can_remove_if_no_refs (vnode)) ! { ! pointer_set_insert (reachable, vnode); ! enqueue_varpool_node (vnode, &first_varpool); ! } ! } ! /* Perform reachability analysis. As a special case do not consider ! extern inline functions not inlined as live because we won't output ! them at all. ! We maintain two worklist, one for cgraph nodes other for varpools and ! are finished once both are empty. */ ! while (first != (struct cgraph_node *) (void *) 1 ! || first_varpool != (struct varpool_node *) (void *) 1) ! { ! if (first != (struct cgraph_node *) (void *) 1) { ! struct cgraph_edge *e; ! node = first; ! first = (struct cgraph_node *) first->symbol.aux; ! if (!pointer_set_contains (reachable, node)) ! node->symbol.aux = (void *)2; ! /* If we found this node reachable, first mark on the callees ! reachable too, unless they are direct calls to extern inline functions ! we decided to not inline. */ ! else { ! for (e = node->callees; e; e = e->next_callee) { ! if (node->analyzed && (!e->inline_failed || !DECL_EXTERNAL (e->callee->symbol.decl) ! || node->alias || before_inlining_p)) pointer_set_insert (reachable, e->callee); ! enqueue_cgraph_node (e->callee, &first, reachable); ! } ! process_references (&node->symbol.ref_list, &first, ! &first_varpool, before_inlining_p, ! reachable); ! ! /* If any function in a comdat group is reachable, force ! all other functions in the same comdat group to be ! also reachable. */ ! if (node->symbol.same_comdat_group ! && !node->global.inlined_to) ! { ! for (next = cgraph (node->symbol.same_comdat_group); ! next != node; ! next = cgraph (next->symbol.same_comdat_group)) ! if (!pointer_set_insert (reachable, next)) ! enqueue_cgraph_node (next, &first, reachable); } } ! /* We can freely remove inline clones even if they are cloned, however if ! function is clone of real clone, we must keep it around in order to ! make materialize_clones produce function body with the changes ! applied. */ ! while (node->clone_of && !node->clone_of->symbol.aux ! && !gimple_has_body_p (node->symbol.decl)) { ! bool noninline = node->clone_of->symbol.decl != node->symbol.decl; ! node = node->clone_of; ! if (noninline && !pointer_set_contains (reachable, node) && !node->symbol.aux) { ! enqueue_cgraph_node (node, &first, reachable); break; } } } - if (first_varpool != (struct varpool_node *) (void *) 1) - { - vnode = first_varpool; - first_varpool = (struct varpool_node *)first_varpool->symbol.aux; - vnode->symbol.aux = NULL; - process_references (&vnode->symbol.ref_list, &first, - &first_varpool, before_inlining_p, - reachable); - /* If any function in a comdat group is reachable, force - all other functions in the same comdat group to be - also reachable. */ - if (vnode->symbol.same_comdat_group) - { - struct varpool_node *next; - for (next = varpool (vnode->symbol.same_comdat_group); - next != vnode; - next = varpool (next->symbol.same_comdat_group)) - if (!pointer_set_insert (reachable, next)) - enqueue_varpool_node (next, &first_varpool); - } - } } ! /* Remove unreachable nodes. ! ! Completely unreachable functions can be fully removed from the callgraph. ! Extern inline functions that we decided to not inline need to become unanalyzed nodes of ! callgraph (so we still have edges to them). We remove function body then. ! ! Also we need to care functions that are unreachable but we need to keep them around ! for later clonning. In this case we also turn them to unanalyzed nodes, but ! keep the body around. */ for (node = cgraph_first_function (); node; node = next) { next = cgraph_next_function (node); - if (node->symbol.aux && !pointer_set_contains (reachable, node)) - { - cgraph_node_remove_callees (node); - ipa_remove_all_references (&node->symbol.ref_list); - node->analyzed = false; - } if (!node->symbol.aux) { - struct cgraph_edge *e; - bool found = false; - int i; - struct ipa_ref *ref; - - node->global.inlined_to = NULL; if (file) fprintf (file, " %s", cgraph_node_name (node)); ! /* See if there is reachable caller. */ ! for (e = node->callers; e && !found; e = e->next_caller) ! if (pointer_set_contains (reachable, e->caller)) ! found = true; ! for (i = 0; (ipa_ref_list_referring_iterate (&node->symbol.ref_list, ! i, ref) ! && !found); i++) ! if (pointer_set_contains (reachable, ref->referring)) ! found = true; ! ! /* If so, we need to keep node in the callgraph. */ ! if (found) ! { ! if (node->analyzed) ! { ! struct cgraph_node *clone; ! ! /* If there are still clones, we must keep body around. ! Otherwise we can just remove the body but keep the clone. */ ! for (clone = node->clones; clone; ! clone = clone->next_sibling_clone) ! if (clone->symbol.aux) ! break; ! if (!clone) ! { ! cgraph_release_function_body (node); ! 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->clone_of) ! node->former_clone_of = node->clone_of->symbol.decl; ! node->clone_of = NULL; ! node->next_sibling_clone = NULL; ! node->prev_sibling_clone = NULL; ! } ! else ! gcc_assert (!clone->symbol.in_other_partition); ! node->analyzed = false; ! changed = true; ! cgraph_node_remove_callees (node); ! ipa_remove_all_references (&node->symbol.ref_list); ! } ! } ! else { ! cgraph_remove_node (node); changed = true; } } } FOR_EACH_FUNCTION (node) { - /* Inline clones might be kept around so their materializing allows further - cloning. If the function the clone is inlined into is removed, we need - to turn it into normal cone. */ if (node->global.inlined_to && !node->callers) { --- 238,363 ---- && (DECL_COMDAT (node->symbol.decl) || DECL_EXTERNAL (node->symbol.decl))))) { gcc_assert (!node->global.inlined_to); pointer_set_insert (reachable, node); + enqueue_node ((symtab_node)node, &first, reachable); } else gcc_assert (!node->symbol.aux); /* Mark variables that are obviously needed. */ ! FOR_EACH_DEFINED_VARIABLE (vnode) ! if (!varpool_can_remove_if_no_refs (vnode)) ! { ! pointer_set_insert (reachable, vnode); ! enqueue_node ((symtab_node)vnode, &first, reachable); ! } ! ! /* Perform reachability analysis. */ ! while (first != (symtab_node) (void *) 1) { ! bool in_boundary_p = !pointer_set_contains (reachable, first); ! symtab_node node = first; ! first = (symtab_node)first->symbol.aux; ! /* If we are processing symbol in boundary, mark its AUX pointer for ! possible later re-processing in enqueue_node. */ ! if (in_boundary_p) ! node->symbol.aux = (void *)2; ! else ! { ! /* If any symbol in a comdat group is reachable, force ! all other in the same comdat group to be also reachable. */ ! if (node->symbol.same_comdat_group) ! { ! symtab_node next; ! for (next = node->symbol.same_comdat_group; ! next != node; ! next = next->symbol.same_comdat_group) ! if (!pointer_set_insert (reachable, next)) ! enqueue_node ((symtab_node) next, &first, reachable); ! } ! /* Mark references as reachable. */ ! process_references (&node->symbol.ref_list, &first, ! before_inlining_p, reachable); ! } ! if (symtab_function_p (node)) { ! struct cgraph_node *cnode = cgraph (node); ! ! /* Mark the callees reachable unless they are direct calls to extern ! inline functions we decided to not inline. */ ! if (!in_boundary_p) { ! struct cgraph_edge *e; ! for (e = cnode->callees; e; e = e->next_callee) { ! if (e->callee->analyzed && (!e->inline_failed || !DECL_EXTERNAL (e->callee->symbol.decl) ! || cnode->alias || before_inlining_p)) pointer_set_insert (reachable, e->callee); ! enqueue_node ((symtab_node) e->callee, &first, reachable); } + + /* When inline clone exists, mark body to be preserved so when removing + offline copy of the function we don't kill it. */ + if (!cnode->alias && cnode->global.inlined_to) + pointer_set_insert (body_needed_for_clonning, cnode->symbol.decl); } ! /* For non-inline clones, force their origins to the boundary and ensure ! that body is not removed. */ ! while (cnode->clone_of && !cnode->clone_of->symbol.aux ! && !gimple_has_body_p (cnode->symbol.decl)) { ! bool noninline = cnode->clone_of->symbol.decl != cnode->symbol.decl; ! cnode = cnode->clone_of; ! if (noninline && !cnode->symbol.aux) { ! pointer_set_insert (body_needed_for_clonning, cnode->symbol.decl); ! enqueue_node ((symtab_node)cnode, &first, reachable); break; } } } } ! /* Remove unreachable functions. */ for (node = cgraph_first_function (); node; node = next) { next = cgraph_next_function (node); if (!node->symbol.aux) { if (file) fprintf (file, " %s", cgraph_node_name (node)); ! cgraph_remove_node (node); ! changed = true; ! } ! else if (!pointer_set_contains (reachable, node)) ! { ! if (node->analyzed) { ! if (file) ! fprintf (file, " %s", cgraph_node_name (node)); ! cgraph_node_remove_callees (node); ! ipa_remove_all_references (&node->symbol.ref_list); changed = true; } + if (!pointer_set_contains (body_needed_for_clonning, node->symbol.decl) + && !DECL_ARTIFICIAL (node->symbol.decl)) + cgraph_release_function_body (node); + node->analyzed = false; } } + + /* Inline clones might be kept around so their materializing allows further + cloning. If the function the clone is inlined into is removed, we need + to turn it into normal cone. */ FOR_EACH_FUNCTION (node) { if (node->global.inlined_to && !node->callers) { *************** cgraph_remove_unreachable_nodes (bool be *** 402,426 **** node->symbol.aux = NULL; } if (file) ! fprintf (file, "\n"); ! ! if (file) ! fprintf (file, "Reclaiming variables:"); for (vnode = varpool_first_variable (); vnode; vnode = vnext) { vnext = varpool_next_variable (vnode); ! if (!pointer_set_contains (reachable, vnode)) ! { if (file) fprintf (file, " %s", varpool_node_name (vnode)); varpool_remove_node (vnode); changed = true; } } ! /* Now update address_taken flags and try to promote functions to be local. */ if (file) fprintf (file, "\nClearing address taken flags:"); FOR_EACH_DEFINED_FUNCTION (node) --- 368,405 ---- node->symbol.aux = NULL; } + /* Remove unreachable variables. */ if (file) ! fprintf (file, "\nReclaiming variables:"); for (vnode = varpool_first_variable (); vnode; vnode = vnext) { vnext = varpool_next_variable (vnode); ! if (!vnode->symbol.aux) ! { if (file) fprintf (file, " %s", varpool_node_name (vnode)); varpool_remove_node (vnode); changed = true; } + else if (!pointer_set_contains (reachable, vnode)) + { + if (vnode->analyzed) + { + if (file) + fprintf (file, " %s", varpool_node_name (vnode)); + changed = true; + } + vnode->analyzed = false; + vnode->symbol.aux = NULL; + } + else + vnode->symbol.aux = NULL; } ! pointer_set_destroy (reachable); ! pointer_set_destroy (body_needed_for_clonning); + /* Now update address_taken flags and try to promote functions to be local. */ if (file) fprintf (file, "\nClearing address taken flags:"); FOR_EACH_DEFINED_FUNCTION (node) *************** cgraph_remove_unreachable_nodes (bool be *** 444,461 **** if (file) fprintf (file, "\n"); - /* Rest of transformations are undesirable at -O0. */ - if (!optimize) - return changed; - #ifdef ENABLE_CHECKING verify_symtab (); #endif /* Reclaim alias pairs for functions that have disappeared from the call graph. */ remove_unreachable_alias_pairs (); - pointer_set_destroy (reachable); return changed; } --- 423,440 ---- if (file) fprintf (file, "\n"); #ifdef ENABLE_CHECKING verify_symtab (); #endif + /* If we removed something, perhaps profile could be improved. */ + if (changed && optimize && inline_edge_summary_vec) + FOR_EACH_DEFINED_FUNCTION (node) + cgraph_propagate_frequency (node); + /* Reclaim alias pairs for functions that have disappeared from the call graph. */ remove_unreachable_alias_pairs (); return changed; } Index: passes.c =================================================================== *** passes.c (revision 187335) --- passes.c (working copy) *************** execute_todo (unsigned int flags) *** 1865,1871 **** if (flags & TODO_remove_functions) { gcc_assert (!cfun); ! cgraph_remove_unreachable_nodes (true, dump_file); } if ((flags & TODO_dump_symtab) && dump_file && !current_function_decl) --- 1865,1871 ---- if (flags & TODO_remove_functions) { gcc_assert (!cfun); ! symtab_remove_unreachable_nodes (true, dump_file); } if ((flags & TODO_dump_symtab) && dump_file && !current_function_decl) *************** execute_one_pass (struct opt_pass *pass) *** 2150,2156 **** bool applied = false; do_per_function (apply_ipa_transforms, (void *)&applied); if (applied) ! cgraph_remove_unreachable_nodes (true, dump_file); /* Restore current_pass. */ current_pass = pass; } --- 2150,2156 ---- bool applied = false; do_per_function (apply_ipa_transforms, (void *)&applied); if (applied) ! symtab_remove_unreachable_nodes (true, dump_file); /* Restore current_pass. */ current_pass = pass; }
On Thu, May 10, 2012 at 1:19 PM, Jan Hubicka <hubicka@ucw.cz> wrote: > Hi, > after some thought, the changes into omp-low are not as obviously harmless as I > originally tought. So i decided to handle this by separate patch. This patch > simply makes cgraph to not release bodies of artificial functions that papers > around the problem in easier way. > > Bootstrapped/regtested x86_64-linux, comitted. > > Honza > > * cgraph.h (cgraph_remove_unreachable_nodes): Rename to ... > (symtab_remove_unreachable_nodes): ... this one. > * ipa-cp.c (ipcp_driver): Do not remove unreachable nodes. > * cgraphunit.c (ipa_passes): Update. > * cgraphclones.c (cgraph_materialize_all_clones): Update. > * cgraph.c (cgraph_release_function_body): Only turn initial > into error mark when initial was previously set. > * ipa-inline.c (ipa_inline): Update. > * ipa.c: Include ipa-inline.h > (enqueue_cgraph_node, enqueue_varpool_node): Remove. > (enqueue_node): New function. > (process_references): Update. > (symtab_remove_unreachable_nodes): Cleanup. > * passes.c (execute_todo, execute_one_pass): Update. This may have caused: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=53321
Index: cgraph.h =================================================================== --- cgraph.h (revision 187335) +++ cgraph.h (working copy) @@ -637,7 +637,7 @@ int compute_call_stmt_bb_frequency (tree void record_references_in_initializer (tree, bool); /* In ipa.c */ -bool cgraph_remove_unreachable_nodes (bool, FILE *); +bool symtab_remove_unreachable_nodes (bool, FILE *); cgraph_node_set cgraph_node_set_new (void); cgraph_node_set_iterator cgraph_node_set_find (cgraph_node_set, struct cgraph_node *); Index: ipa-cp.c =================================================================== --- ipa-cp.c (revision 187335) +++ ipa-cp.c (working copy) @@ -2445,7 +2445,6 @@ ipcp_driver (void) struct cgraph_2edge_hook_list *edge_duplication_hook_holder; struct topo_info topo; - cgraph_remove_unreachable_nodes (true,dump_file); ipa_check_create_node_params (); ipa_check_create_edge_args (); grow_next_edge_clone_vector (); Index: omp-low.c =================================================================== --- omp-low.c (revision 187335) +++ omp-low.c (working copy) @@ -1572,7 +1572,6 @@ create_omp_child_function (omp_context * DECL_UNINLINABLE (decl) = 1; DECL_EXTERNAL (decl) = 0; DECL_CONTEXT (decl) = NULL_TREE; - DECL_INITIAL (decl) = make_node (BLOCK); t = build_decl (DECL_SOURCE_LOCATION (decl), RESULT_DECL, NULL_TREE, void_type_node); @@ -1605,13 +1604,6 @@ create_omp_child_function (omp_context * DECL_CHAIN (t) = DECL_ARGUMENTS (decl); DECL_ARGUMENTS (decl) = t; } - - /* Allocate memory for the function structure. The call to - allocate_struct_function clobbers CFUN, so we need to restore - it afterward. */ - push_struct_function (decl); - cfun->function_end_locus = gimple_location (ctx->stmt); - pop_cfun (); } @@ -3241,28 +3233,31 @@ remove_exit_barrier (struct omp_region * unsigned ix; any_addressable_vars = 0; - FOR_EACH_LOCAL_DECL (DECL_STRUCT_FUNCTION (child_fun), ix, decl) - if (TREE_ADDRESSABLE (decl)) - { - any_addressable_vars = 1; - break; - } - for (block = gimple_block (stmt); - !any_addressable_vars - && block - && TREE_CODE (block) == BLOCK; - block = BLOCK_SUPERCONTEXT (block)) + if (DECL_STRUCT_FUNCTION (child_fun)) { - for (local_decls = BLOCK_VARS (block); - local_decls; - local_decls = DECL_CHAIN (local_decls)) - if (TREE_ADDRESSABLE (local_decls)) + FOR_EACH_LOCAL_DECL (DECL_STRUCT_FUNCTION (child_fun), ix, decl) + if (TREE_ADDRESSABLE (decl)) { any_addressable_vars = 1; break; } - if (block == gimple_block (parallel_stmt)) - break; + for (block = gimple_block (stmt); + !any_addressable_vars + && block + && TREE_CODE (block) == BLOCK; + block = BLOCK_SUPERCONTEXT (block)) + { + for (local_decls = BLOCK_VARS (block); + local_decls; + local_decls = DECL_CHAIN (local_decls)) + if (TREE_ADDRESSABLE (local_decls)) + { + any_addressable_vars = 1; + break; + } + if (block == gimple_block (parallel_stmt)) + break; + } } } if (!any_addressable_vars) @@ -3370,6 +3365,14 @@ expand_omp_taskreg (struct omp_region *r entry_stmt = last_stmt (region->entry); child_fn = gimple_omp_taskreg_child_fn (entry_stmt); + + /* Allocate memory for the function structure. The call to + allocate_struct_function clobbers CFUN, so we need to restore + it afterward. */ + push_struct_function (child_fn); + DECL_INITIAL (child_fn) = make_node (BLOCK); + pop_cfun (); + child_cfun = DECL_STRUCT_FUNCTION (child_fn); /* If this function has been already instrumented, make sure the child function isn't instrumented again. */ @@ -6685,6 +6688,14 @@ lower_omp_taskreg (gimple_stmt_iterator par_bind = gimple_seq_first_stmt (gimple_omp_body (stmt)); par_body = gimple_bind_body (par_bind); child_fn = ctx->cb.dst_fn; + + /* Allocate memory for the function structure. The call to + allocate_struct_function clobbers CFUN, so we need to restore + it afterward. */ + push_struct_function (child_fn); + DECL_INITIAL (child_fn) = make_node (BLOCK); + cfun->function_end_locus = gimple_location (ctx->stmt); + pop_cfun (); if (gimple_code (stmt) == GIMPLE_OMP_PARALLEL && !gimple_omp_parallel_combined_p (stmt)) { Index: cgraphunit.c =================================================================== --- cgraphunit.c (revision 187335) +++ cgraphunit.c (working copy) @@ -1836,7 +1836,7 @@ ipa_passes (void) because TODO is run before the subpasses. It is important to remove the unreachable functions to save works at IPA level and to get LTO symbol tables right. */ - cgraph_remove_unreachable_nodes (true, cgraph_dump_file); + symtab_remove_unreachable_nodes (true, cgraph_dump_file); /* If pass_all_early_optimizations was not scheduled, the state of the cgraph will not be properly updated. Update it now. */ @@ -1962,7 +1962,7 @@ compile (void) /* This pass remove bodies of extern inline functions we never inlined. Do this later so other IPA passes see what is really going on. */ - cgraph_remove_unreachable_nodes (false, dump_file); + symtab_remove_unreachable_nodes (false, dump_file); cgraph_global_info_ready = true; if (cgraph_dump_file) { @@ -1987,7 +1987,7 @@ compile (void) cgraph_materialize_all_clones (); bitmap_obstack_initialize (NULL); execute_ipa_pass_list (all_late_ipa_passes); - cgraph_remove_unreachable_nodes (true, dump_file); + symtab_remove_unreachable_nodes (true, dump_file); #ifdef ENABLE_CHECKING verify_symtab (); #endif Index: cgraphclones.c =================================================================== --- cgraphclones.c (revision 187335) +++ cgraphclones.c (working copy) @@ -870,7 +870,7 @@ cgraph_materialize_all_clones (void) #ifdef ENABLE_CHECKING verify_cgraph (); #endif - cgraph_remove_unreachable_nodes (false, cgraph_dump_file); + symtab_remove_unreachable_nodes (false, cgraph_dump_file); } #include "gt-cgraphclones.h" Index: ipa-inline.c =================================================================== --- ipa-inline.c (revision 187335) +++ ipa-inline.c (working copy) @@ -1717,7 +1717,7 @@ ipa_inline (void) } inline_small_functions (); - cgraph_remove_unreachable_nodes (true, dump_file); + symtab_remove_unreachable_nodes (true, dump_file); free (order); /* We already perform some inlining of functions called once during Index: ipa.c =================================================================== --- ipa.c (revision 187335) +++ ipa.c (working copy) @@ -33,6 +33,7 @@ along with GCC; see the file COPYING3. #include "tree-iterator.h" #include "ipa-utils.h" #include "pointer-set.h" +#include "ipa-inline.h" /* Look for all functions inlined to NODE and update their inlined_to pointers to INLINED_TO. */ @@ -49,7 +50,7 @@ update_inlined_to_pointer (struct cgraph } } -/* Add cgraph NODE to queue starting at FIRST. +/* Add symtab NODE to queue starting at FIRST. The queue is linked via AUX pointers and terminated by pointer to 1. We enqueue nodes at two occasions: when we find them reachable or when we find @@ -58,8 +59,8 @@ update_inlined_to_pointer (struct cgraph reachable. */ static void -enqueue_cgraph_node (struct cgraph_node *node, struct cgraph_node **first, - struct pointer_set_t *reachable) +enqueue_node (symtab_node node, symtab_node *first, + struct pointer_set_t *reachable) { /* Node is still in queue; do nothing. */ if (node->symbol.aux && node->symbol.aux != (void *) 2) @@ -72,21 +73,11 @@ enqueue_cgraph_node (struct cgraph_node *first = node; } -/* Add varpool NODE to queue starting at FIRST. */ - -static void -enqueue_varpool_node (struct varpool_node *node, struct varpool_node **first) -{ - node->symbol.aux = *first; - *first = node; -} - /* Process references. */ static void process_references (struct ipa_ref_list *list, - struct cgraph_node **first, - struct varpool_node **first_varpool, + symtab_node *first, bool before_inlining_p, struct pointer_set_t *reachable) { @@ -97,18 +88,21 @@ process_references (struct ipa_ref_list if (symtab_function_p (ref->referred)) { struct cgraph_node *node = ipa_ref_node (ref); + if (node->analyzed && (!DECL_EXTERNAL (node->symbol.decl) || node->alias || before_inlining_p)) pointer_set_insert (reachable, node); - enqueue_cgraph_node (node, first, reachable); + enqueue_node ((symtab_node) node, first, reachable); } else { struct varpool_node *node = ipa_ref_varpool_node (ref); - if (!pointer_set_insert (reachable, node)) - enqueue_varpool_node (node, first_varpool); + + if (node->analyzed) + pointer_set_insert (reachable, node); + enqueue_node ((symtab_node) node, first, reachable); } } } @@ -162,19 +156,63 @@ has_addr_references_p (struct cgraph_nod } /* Perform reachability analysis and reclaim all unreachable nodes. - If BEFORE_INLINING_P is true this function is called before inlining - decisions has been made. If BEFORE_INLINING_P is false this function also - removes unneeded bodies of extern inline functions. */ + + The algorithm is basically mark&sweep but with some extra refinements: + + - reachable extern inline functions needs special handling; the bodies needs + to stay in memory until inlining in hope that they will be inlined. + After inlining we release their bodies and turn them into unanalyzed + nodes even when they are reachable. + + BEFORE_INLINING_P specify whether we are before or after inlining. + + - virtual functions are kept in callgraph even if they seem unreachable in + hope calls to them will be devirtualized. + + Again we remove them after inlining. In late optimization some + devirtualization may happen, but it is not importnat since we won't inline + the call. In theory early opts and IPA should work out all important cases. + + - virtual clones needs bodies of their origins for later materialization; + this means that we want to keep the body even if the origin is unreachable + otherwise. To avoid origin from sitting in the callgraph and being + walked by IPA passes, we turn them into unanalyzed nodes with body + defined. + + We maintain set of function declaration where body needs to stay in + body_needed_for_clonning + + Inline clones represent special case: their declaration match the + declaration of origin and cgraph_remove_node already knows how to + reshape callgraph and preserve body when offline copy of function or + inline clone is being removed. + + We maintain queue of both reachable symbols (i.e. defined symbols that needs + to stay) and symbols that are in boundary (i.e. external symbols referenced + by reachable symbols or origins of clones). The queue is represented + as linked list by AUX pointer terminated by 1. + + A the end we keep all reachable symbols. For symbols in boundary we always + turn definition into a declaration, but we may keep function body around + based on body_needed_for_clonning + + All symbols that enter the queue have AUX pointer non-zero and are in the + boundary. Pointer set REACHABLE is used to track reachable symbols. + + Every symbol can be visited twice - once as part of boundary and once + as real reachable symbol. enqueue_node needs to decide whether the + node needs to be re-queued for second processing. For this purpose + we set AUX pointer of processed symbols in the boundary to constant 2. */ bool -cgraph_remove_unreachable_nodes (bool before_inlining_p, FILE *file) +symtab_remove_unreachable_nodes (bool before_inlining_p, FILE *file) { - struct cgraph_node *first = (struct cgraph_node *) (void *) 1; - struct varpool_node *first_varpool = (struct varpool_node *) (void *) 1; + symtab_node first = (symtab_node) (void *) 1; struct cgraph_node *node, *next; struct varpool_node *vnode, *vnext; bool changed = false; struct pointer_set_t *reachable = pointer_set_create (); + struct pointer_set_t *body_needed_for_clonning = pointer_set_create (); #ifdef ENABLE_CHECKING verify_symtab (); @@ -191,8 +229,8 @@ cgraph_remove_unreachable_nodes (bool be This is mostly when they can be referenced externally. Inline clones are special since their declarations are shared with master clone and thus cgraph_can_remove_if_no_direct_calls_and_refs_p should not be called on them. */ - FOR_EACH_FUNCTION (node) - if (node->analyzed && !node->global.inlined_to + FOR_EACH_DEFINED_FUNCTION (node) + if (!node->global.inlined_to && (!cgraph_can_remove_if_no_direct_calls_and_refs_p (node) /* Keep around virtual functions for possible devirtualization. */ || (before_inlining_p @@ -200,198 +238,125 @@ cgraph_remove_unreachable_nodes (bool be && (DECL_COMDAT (node->symbol.decl) || DECL_EXTERNAL (node->symbol.decl))))) { gcc_assert (!node->global.inlined_to); - enqueue_cgraph_node (node, &first, reachable); pointer_set_insert (reachable, node); + enqueue_node ((symtab_node)node, &first, reachable); } else gcc_assert (!node->symbol.aux); /* Mark variables that are obviously needed. */ - FOR_EACH_VARIABLE (vnode) + FOR_EACH_DEFINED_VARIABLE (vnode) + if (!varpool_can_remove_if_no_refs (vnode)) + { + pointer_set_insert (reachable, vnode); + enqueue_node ((symtab_node)vnode, &first, reachable); + } + + /* Perform reachability analysis. */ + while (first != (symtab_node) (void *) 1) { - if ((vnode->analyzed || vnode->symbol.force_output) - && !varpool_can_remove_if_no_refs (vnode)) - { - pointer_set_insert (reachable, vnode); - enqueue_varpool_node (vnode, &first_varpool); - } - } + bool in_boundary_p = !pointer_set_contains (reachable, first); + symtab_node node = first; - /* Perform reachability analysis. As a special case do not consider - extern inline functions not inlined as live because we won't output - them at all. + first = (symtab_node)first->symbol.aux; - We maintain two worklist, one for cgraph nodes other for varpools and - are finished once both are empty. */ + /* If we are processing symbol in boundary, mark its AUX pointer for + possible later re-processing in enqueue_node. */ + if (in_boundary_p) + node->symbol.aux = (void *)2; + else + { + /* If any symbol in a comdat group is reachable, force + all other in the same comdat group to be also reachable. */ + if (node->symbol.same_comdat_group) + { + symtab_node next; + for (next = node->symbol.same_comdat_group; + next != node; + next = next->symbol.same_comdat_group) + if (!pointer_set_insert (reachable, next)) + enqueue_node ((symtab_node) next, &first, reachable); + } + /* Mark references as reachable. */ + process_references (&node->symbol.ref_list, &first, + before_inlining_p, reachable); + } - while (first != (struct cgraph_node *) (void *) 1 - || first_varpool != (struct varpool_node *) (void *) 1) - { - if (first != (struct cgraph_node *) (void *) 1) + if (symtab_function_p (node)) { - struct cgraph_edge *e; - node = first; - first = (struct cgraph_node *) first->symbol.aux; - if (!pointer_set_contains (reachable, node)) - node->symbol.aux = (void *)2; - /* If we found this node reachable, first mark on the callees - reachable too, unless they are direct calls to extern inline functions - we decided to not inline. */ - else + struct cgraph_node *cnode = cgraph (node); + + /* Mark the callees reachable unless they are direct calls to extern + inline functions we decided to not inline. */ + if (!in_boundary_p) { - for (e = node->callees; e; e = e->next_callee) + struct cgraph_edge *e; + for (e = cnode->callees; e; e = e->next_callee) { - if (node->analyzed + if (e->callee->analyzed && (!e->inline_failed || !DECL_EXTERNAL (e->callee->symbol.decl) - || node->alias + || cnode->alias || before_inlining_p)) pointer_set_insert (reachable, e->callee); - enqueue_cgraph_node (e->callee, &first, reachable); - } - process_references (&node->symbol.ref_list, &first, - &first_varpool, before_inlining_p, - reachable); - - /* If any function in a comdat group is reachable, force - all other functions in the same comdat group to be - also reachable. */ - if (node->symbol.same_comdat_group - && !node->global.inlined_to) - { - for (next = cgraph (node->symbol.same_comdat_group); - next != node; - next = cgraph (next->symbol.same_comdat_group)) - if (!pointer_set_insert (reachable, next)) - enqueue_cgraph_node (next, &first, reachable); + enqueue_node ((symtab_node) e->callee, &first, reachable); } + + /* When inline clone exists, mark body to be preserved so when removing + offline copy of the function we don't kill it. */ + if (!cnode->alias && cnode->global.inlined_to) + pointer_set_insert (body_needed_for_clonning, cnode->symbol.decl); } - /* We can freely remove inline clones even if they are cloned, however if - function is clone of real clone, we must keep it around in order to - make materialize_clones produce function body with the changes - applied. */ - while (node->clone_of && !node->clone_of->symbol.aux - && !gimple_has_body_p (node->symbol.decl)) + /* For non-inline clones, force their origins to the boundary and ensure + that body is not removed. */ + while (cnode->clone_of && !cnode->clone_of->symbol.aux + && !gimple_has_body_p (cnode->symbol.decl)) { - bool noninline = node->clone_of->symbol.decl != node->symbol.decl; - node = node->clone_of; - if (noninline && !pointer_set_contains (reachable, node) && !node->symbol.aux) + bool noninline = cnode->clone_of->symbol.decl != cnode->symbol.decl; + cnode = cnode->clone_of; + if (noninline && !cnode->symbol.aux) { - enqueue_cgraph_node (node, &first, reachable); + pointer_set_insert (body_needed_for_clonning, cnode->symbol.decl); + enqueue_node ((symtab_node)cnode, &first, reachable); break; } } } - if (first_varpool != (struct varpool_node *) (void *) 1) - { - vnode = first_varpool; - first_varpool = (struct varpool_node *)first_varpool->symbol.aux; - vnode->symbol.aux = NULL; - process_references (&vnode->symbol.ref_list, &first, - &first_varpool, before_inlining_p, - reachable); - /* If any function in a comdat group is reachable, force - all other functions in the same comdat group to be - also reachable. */ - if (vnode->symbol.same_comdat_group) - { - struct varpool_node *next; - for (next = varpool (vnode->symbol.same_comdat_group); - next != vnode; - next = varpool (next->symbol.same_comdat_group)) - if (!pointer_set_insert (reachable, next)) - enqueue_varpool_node (next, &first_varpool); - } - } } - /* Remove unreachable nodes. - - Completely unreachable functions can be fully removed from the callgraph. - Extern inline functions that we decided to not inline need to become unanalyzed nodes of - callgraph (so we still have edges to them). We remove function body then. - - Also we need to care functions that are unreachable but we need to keep them around - for later clonning. In this case we also turn them to unanalyzed nodes, but - keep the body around. */ + /* Remove unreachable functions. */ for (node = cgraph_first_function (); node; node = next) { next = cgraph_next_function (node); - if (node->symbol.aux && !pointer_set_contains (reachable, node)) - { - cgraph_node_remove_callees (node); - ipa_remove_all_references (&node->symbol.ref_list); - node->analyzed = false; - } if (!node->symbol.aux) { - struct cgraph_edge *e; - bool found = false; - int i; - struct ipa_ref *ref; - - node->global.inlined_to = NULL; if (file) fprintf (file, " %s", cgraph_node_name (node)); - /* See if there is reachable caller. */ - for (e = node->callers; e && !found; e = e->next_caller) - if (pointer_set_contains (reachable, e->caller)) - found = true; - for (i = 0; (ipa_ref_list_referring_iterate (&node->symbol.ref_list, - i, ref) - && !found); i++) - if (pointer_set_contains (reachable, ref->referring)) - found = true; - - /* If so, we need to keep node in the callgraph. */ - if (found) - { - if (node->analyzed) - { - struct cgraph_node *clone; - - /* If there are still clones, we must keep body around. - Otherwise we can just remove the body but keep the clone. */ - for (clone = node->clones; clone; - clone = clone->next_sibling_clone) - if (clone->symbol.aux) - break; - if (!clone) - { - cgraph_release_function_body (node); - 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->clone_of) - node->former_clone_of = node->clone_of->symbol.decl; - node->clone_of = NULL; - node->next_sibling_clone = NULL; - node->prev_sibling_clone = NULL; - } - else - gcc_assert (!clone->symbol.in_other_partition); - node->analyzed = false; - changed = true; - cgraph_node_remove_callees (node); - ipa_remove_all_references (&node->symbol.ref_list); - } - } - else + cgraph_remove_node (node); + changed = true; + } + else if (!pointer_set_contains (reachable, node)) + { + if (node->analyzed) { - cgraph_remove_node (node); + if (file) + fprintf (file, " %s", cgraph_node_name (node)); + cgraph_node_remove_callees (node); + ipa_remove_all_references (&node->symbol.ref_list); changed = true; } + if (!pointer_set_contains (body_needed_for_clonning, node->symbol.decl)) + cgraph_release_function_body (node); + node->analyzed = false; } } + + /* Inline clones might be kept around so their materializing allows further + cloning. If the function the clone is inlined into is removed, we need + to turn it into normal cone. */ FOR_EACH_FUNCTION (node) { - /* Inline clones might be kept around so their materializing allows further - cloning. If the function the clone is inlined into is removed, we need - to turn it into normal cone. */ if (node->global.inlined_to && !node->callers) { @@ -402,25 +367,38 @@ cgraph_remove_unreachable_nodes (bool be node->symbol.aux = NULL; } + /* Remove unreachable variables. */ if (file) - fprintf (file, "\n"); - - if (file) - fprintf (file, "Reclaiming variables:"); + fprintf (file, "\nReclaiming variables:"); for (vnode = varpool_first_variable (); vnode; vnode = vnext) { vnext = varpool_next_variable (vnode); - if (!pointer_set_contains (reachable, vnode)) - { + if (!vnode->symbol.aux) + { if (file) fprintf (file, " %s", varpool_node_name (vnode)); varpool_remove_node (vnode); changed = true; } + else if (!pointer_set_contains (reachable, vnode)) + { + if (vnode->analyzed) + { + if (file) + fprintf (file, " %s", varpool_node_name (vnode)); + changed = true; + } + vnode->analyzed = false; + vnode->symbol.aux = NULL; + } + else + vnode->symbol.aux = NULL; } - /* Now update address_taken flags and try to promote functions to be local. */ + pointer_set_destroy (reachable); + pointer_set_destroy (body_needed_for_clonning); + /* Now update address_taken flags and try to promote functions to be local. */ if (file) fprintf (file, "\nClearing address taken flags:"); FOR_EACH_DEFINED_FUNCTION (node) @@ -444,18 +422,18 @@ cgraph_remove_unreachable_nodes (bool be if (file) fprintf (file, "\n"); - /* Rest of transformations are undesirable at -O0. */ - if (!optimize) - return changed; - #ifdef ENABLE_CHECKING verify_symtab (); #endif + /* If we removed something, perhaps profile could be improved. */ + if (changed && optimize && inline_edge_summary_vec) + FOR_EACH_DEFINED_FUNCTION (node) + cgraph_propagate_frequency (node); + /* Reclaim alias pairs for functions that have disappeared from the call graph. */ remove_unreachable_alias_pairs (); - pointer_set_destroy (reachable); return changed; } Index: passes.c =================================================================== --- passes.c (revision 187335) +++ passes.c (working copy) @@ -1865,7 +1865,7 @@ execute_todo (unsigned int flags) if (flags & TODO_remove_functions) { gcc_assert (!cfun); - cgraph_remove_unreachable_nodes (true, dump_file); + symtab_remove_unreachable_nodes (true, dump_file); } if ((flags & TODO_dump_symtab) && dump_file && !current_function_decl) @@ -2150,7 +2150,7 @@ execute_one_pass (struct opt_pass *pass) bool applied = false; do_per_function (apply_ipa_transforms, (void *)&applied); if (applied) - cgraph_remove_unreachable_nodes (true, dump_file); + symtab_remove_unreachable_nodes (true, dump_file); /* Restore current_pass. */ current_pass = pass; }