This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: Symbol table 20/many: cleanup of cgraph_remove_unreachable_nodes
- From: Jan Hubicka <hubicka at ucw dot cz>
- To: Jan Hubicka <hubicka at ucw dot cz>
- Cc: gcc-patches at gcc dot gnu dot org
- Date: Thu, 10 May 2012 22:19:31 +0200
- Subject: Re: Symbol table 20/many: cleanup of cgraph_remove_unreachable_nodes
- References: <20120509151515.GA1951@kam.mff.cuni.cz>
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;
}